상품을 작성할 때, text 길이에 따라서 TextField와 layer의 width를 동적으로 변화해야 한다.
따라서 TextField를 커스텀하기로 했다.
// in TextField
private func bind() {
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: self)
.compactMap { ($0.object as? UITextField)?.text }
.map { text in
let textWidth = text.size(withAttributes: [.font: self.font!]).width
return max(100, textWidth + 40)
}
.sink { [weak self] newWidth in
self?.updateLayout(with: newWidth)
}
.store(in: &self.subscriptions)
}
private func updateLayout(with newWidth: CGFloat) {
self.widthConstraint?.constant = newWidth
}
NotificationCenter를 통해 textDidChangeNotification가 감지될 때마다 textString을 받는다. textString은 font에 맞게 width를 계산한다. UX를 위해 좌우 합산 20 + 20 = 40 의 여백을 만들고, 최소 width가 100을 넘기도록 max를 통해 newWidth를 만든다. 오토레이아웃을 사용하고 있기 때문에 widthConstraint값을 설정한다.
textField의 width가 변경되었으니, layer의 width를 변경해주어야 한다.
// in TextField
private var previousWidth: CGFloat = 0
private let bezierPath = UIBezierPath()
private let dashLayer = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
let shouldUpdateDashedBorder = self.previousWidth != bounds.width
if shouldUpdateDashedBorder {
self.previousWidth = bounds.width
self.updateDashedBorder()
}
}
private func updateDashedBorder() {
bezierPath.removeAllPoints()
bezierPath.move(to: CGPoint(x: .zero, y: bounds.height - self.borderHeight))
bezierPath.addLine(to: CGPoint(x: bounds.width, y: bounds.height - self.borderHeight))
dashLayer.path = bezierPath.cgPath
}
private func setupStyle(borderColor: UIColor) {
self.dashLayer.strokeColor = borderColor.cgColor
self.dashLayer.frame.size.height = 1
self.dashLayer.strokeColor = UIColor.black.cgColor
self.dashLayer.lineWidth = 1
self.dashLayer.fillColor = nil
self.dashLayer.lineDashPattern = [2, 2]
self.backgroundColor = .black.withAlphaComponent(0.1) // to erase
}
layoutSubviews
textField의 widthConstraints를 변경하고 오토레이아웃이 설정되면 layoutSubviews가 호출되므로, bounds를 통해 TextField의 최신 width를 알 수 있다. 따라서 layoutSubviews에서 TextField의 최신 width를 통해 layer의 width를 변경한다.
CAShapeLayer
CAShapeLayer를 사용해서 dash style을 설정한다.