iOS) Dynamic TextView의 Underline - TextView에 밑줄 추가하기
textField에 under line을 생성하는 예제는 많다.
dynamic textView를 생성하는 예제도 많다.
둘을 합쳐보자.
우선 textView에 under line을 추가하는 코드이다.
func underLine() {
let border = CALayer()
border.frame = CGRect(x: 0, y: self.frame.size.height-1, width: self.frame.width, height: 1)
border.borderWidth = 1
border.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(border)
}
간단하게 layer를 textView의 frame에 맞추어 만들어 sublayer로 추가 하는것이다.
참고로 textView의 frame을 얻기 위해 viewDidLoad() 가 아닌 viewWillLayoutSubviews()에서 추가해 주거나, textView를 추가 한 후 layoutifNeeded()를 통해 view의 layout을 업데이트 해준 후 추가해 주어야 한다.
textView의 크기를 text에 맞추어 갱신하는 코드이다.
func automaticallySizeToFit(superFrame: CGSize) {
let initalSize = CGSize(width: superFrame.width, height: 1000)
let estimateSize = dynamicTextView.sizeThatFits(initalSize)
dynamicTextView.constraints.forEach { (constraint) in
if constraint.firstAttribute == .height {
constraint.constant = estimateSize.height
}
}
}
sizeTahtFits() 함수는 적용되는 view에 대해 최적화 된 size를 리턴 해준다.
그 후 textView의 height constraint를 찾아 최적화 된 size의 height로 변환 해준다.
자 그럼 dynamicTextView에 layer를 추가 해서 밑줄이 있는 dynamicTextView를 만들어보자.
엥
밑줄이 변화되는 view의 크기에 따라서 움직이지 않는다.
layer는 view가 아니다. layer를 생성시 autoLayout이 아닌 '값'으로 지정해야 한다.
따라서 밑줄은 생성시 frame 값 그대로 남아있게 된다.
textView의 bottom에 밑줄이 고정되는 형태로 만들어보자.
textViewDelegate를 통해 view 크기가 업데이트 될때 layer도 같이 업데이트 해보기
layer를 global property로 생성하여 textViewDidChange(_ textView: UITextView)에서 업데이트 해줘보자.
물론 객체지향에 어긋나지만 일단 해보자.
func automaticallySizeToFit(superFrame: CGSize) {
let initalSize = CGSize(width: 350, height: 1000)
let estimateSize = dynamicTextView.sizeThatFits(initalSize)
// 요기서 밑줄 업데이트
border.frame = CGRect(x: 0, y: estimateSize.height - 3, width: dynamicTextView.frame.width, height: 1)
dynamicTextView.constraints.forEach { (constraint) in
if constraint.firstAttribute == .height {
constraint.constant = estimateSize.height
}
}
}
실패~
우리에겐 autoLayout이 필요하다.
CALayer가 아닌 UIView의 형태로 underline 만들기
view형태의 underline을 만들어 autoLayout을 적용시킨다면 되지 않을까...
func underLine() {
let underLineView = UIView()
self.addSubview(underLineView)
underLineView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
underLineView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
underLineView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
underLineView.widthAnchor.constraint(equalToConstant: 100),
underLineView.heightAnchor.constraint(equalToConstant: 1)
])
underLineView.layer.borderWidth = 1
underLineView.layer.backgroundColor = UIColor.black.cgColor
}
UITextView에 extension을 통해 추가 해주었다.
결과는...?
읭?..
아마 textView의 scrollView의 재사용 그 머시기 때문에 topAnchor 바로 위에 bottom Anchor가 잡히지 않았나 싶다..
최후의 수단 StackView로 감싸기
마지막 방법은 textView와 UnderLine 형태의 View를 stackView로 감싸는 것이다.
var containerStackView: UIStackView = {
var stackView = UIStackView()
stackView.backgroundColor = .blue
stackView.axis = .vertical
stackView.alignment = .fill
stackView.distribution = .fill
stackView.spacing = 0
return stackView
}()
var textView: UITextView = {
var textView = UITextView()
textView.text = "dynamic textView with underline"
textView.font = UIFont.boldSystemFont(ofSize: 20)
return textView
}()
var underLine: UIView = {
var view = UIView()
view.layer.borderWidth = 1
view.layer.borderColor = UIColor.black.cgColor
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
view.addSubview(containerStackView)
containerStackView.addArrangedSubview(textView)
containerStackView.addArrangedSubview(underLine)
containerStackView.translatesAutoresizingMaskIntoConstraints = false
textView.translatesAutoresizingMaskIntoConstraints = false
underLine.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
containerStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
containerStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
containerStackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
textView.widthAnchor.constraint(equalTo: containerStackView.widthAnchor),
textView.heightAnchor.constraint(equalToConstant: 100),
underLine.widthAnchor.constraint(equalTo: containerStackView.widthAnchor),
underLine.heightAnchor.constraint(equalToConstant: 1)
])
view.layoutIfNeeded()
textViewDidChange(textView)
}
성공