Доброго времени суток))
Нужна помощь!
В общем пытаюсь сделать UITextView на максималках, с нижним и верхним плейсхолдером и пока все работает, но потерялся в анимации перехода плейсхолдера из текста в текствью на верх!
Код что ниже сыроват)) И очень буду благодарен за правки по фрейма, и за советы или может за доработку анимации, всем спасибо)))
@IBDesignable class CustomTextView: UITextView, UITextViewDelegate {
@IBInspectable var textColor_: UIColor = .black
//set text indents
@IBInspectable var fieldTopIndent: CGFloat = 4
@IBInspectable var fieldBottomIndent: CGFloat = 4
@IBInspectable var fieldRightIndent: CGFloat = 4
@IBInspectable var fieldLeftIndent: CGFloat {
get { return textContainerInset.left }
set { textContainerInset.left = newValue }
}
//set top placeholder label style parameters
@IBInspectable var placeholderText: String {
get { return placeholderLabel.text ?? "" }
set { placeholderLabel.text = newValue }
}
@IBInspectable var placeholderTextColor: UIColor {
get { return placeholderLabel.textColor }
set { placeholderLabel.textColor = newValue }
}
@IBInspectable var placeholderFont: UIFont {
get { return placeholderLabel.font }
set { placeholderLabel.font = newValue }
}
//set bottom placeholder label parameters
@IBInspectable var bottomPlaceholderText: String {
get { return bottomPlaceholderLabel.text ?? "" }
set { bottomPlaceholderLabel.text = newValue }
}
@IBInspectable var bottomPlaceholderTextColor: UIColor {
get { return bottomPlaceholderLabel.textColor }
set { bottomPlaceholderLabel.textColor = newValue }
}
@IBInspectable var bottomPlaceholderFont: UIFont {
get { return bottomPlaceholderLabel.font }
set { bottomPlaceholderLabel.font = newValue }
}
@IBInspectable var bottomPlaceholderTopIndent: CGFloat = 4
//set separator style
@IBInspectable var separatorHeight: CGFloat = 0
@IBInspectable var separatorColor: UIColor? {
get { return separator.backgroundColor }
set { separator.backgroundColor = newValue }
}
//set clear button image
@IBInspectable var clearButtonHeight: CGFloat = 8
@IBInspectable var clearButtonRigthIndent: CGFloat = 4
@IBInspectable var withClearButton: Bool = true
@IBInspectable var clearButtonImage: UIImage? {
get { return clearButton.imageView?.image }
set { clearButton.setImage(newValue, for: []) }
}
private var clearImage: UIImage? {
guard
let data = Data(base64Encoded: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAAGXcA1uAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAGKADAAQAAAABAAAAGAAAAADiNXWtAAABf0lEQVRIDa2TS04EMQxEBzYsucFwHm4ViQOxA4m7MOdgB36ZLsupdOYjYSnyr6qcT/fhUOxhi3/xj6XRw3cV6DzF6jA4BJ+xugGj8HFOe9yRW56FDtAUEEhpC+hP1lQBLU3Fz96kcaQoTTGyxnAVX6iGKT+0SDqdalg2SH62gg5JTQaQ1VTAA1QD7zm1Oi3Ss90MBF4neDxstRm4jvaJw1PkLTPObADSe401FTdS23w6wNqzkwCrlwQV5EWqYHonMQAILP9mtW+B5fdIIk/gS6QlGFKLJdXqdSYwaS2iCvJ4IDmY29g7U5KqWl7dDmn4niBVcKTdNGkAq7nyuRUH8InUP28JNGKLXMcinqwKCyjP1leDWvSEc08vDQH/r5xQB7XAe7/m3PUx1mS3DKpCHi+FfdK9g/j8d3fswp77H+o7Jv+KtXoj18u8RbQndqlW3yiFPLgmzFWw47sHXRP2x7vljaYT7Q1x4dj8YKtBk3hltUiuCVc8sQZdFHbSv+V/XSgDfYCuWLoAAAAASUVORK5CYII=", options: .ignoreUnknownCharacters),
let image = UIImage(data: data)
else { return nil }
return image
}
private var isPlaceholder: Bool {
return self.textColor == placeholderTextColor
}
private let separator = UIView()
private let placeholderLabel = UILabel()
private let bottomPlaceholderLabel = UILabel()
private let clearButton = UIButton()
var height: ((CGFloat) -> ())?
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
self.separator.layer.cornerRadius = separatorHeight / 2
self.setFrames()
self.height?(prefferedHeight)
}
override func draw(_ rect: CGRect) {
super.draw(rect)
checkPlaceholder()
print("super.draw(rect)")
}
func textViewDidChange(_ textView: UITextView) {
self.height?(prefferedHeight)
clearButton.isHidden = text.isEmpty
}
func textViewDidBeginEditing(_ textView: UITextView) {
if isPlaceholder {
text = ""
textColor = textColor_
placeholderLabel.alpha = 1
layoutSubviews()
}
clearButton.isHidden = text.isEmpty
}
func textViewDidEndEditing(_ textView: UITextView) {
checkPlaceholder()
clearButton.isHidden = true
}
}
private extension CustomTextView {
func commonInit() {
textContainer.lineFragmentPadding = 0
textContainerInset = .zero
delegate = self
addSubview(placeholderLabel)
addSubview(bottomPlaceholderLabel)
addSubview(separator)
addSubview(clearButton)
placeholderLabel.numberOfLines = 0
bottomPlaceholderLabel.numberOfLines = 0
clearButton.setImage(clearImage, for: [])
clearButton.tintColor = .black
clearButton.addTarget(self, action: #selector(clearButtonDidTap), for: .touchUpInside)
clearButton.isHidden = true
fieldTopIndent = 4
fieldBottomIndent = 4
fieldLeftIndent = 4
fieldRightIndent = 4
}
func checkPlaceholder() {
placeholderLabel.alpha = text.isEmpty ? 0 : 1
textColor = text.isEmpty ? placeholderTextColor : textColor_
if text.isEmpty { text = placeholderText }
layoutSubviews()
}
var prefferedHeight: CGFloat {
let size = CGSize(width: bounds.width, height: .infinity)
let estimatedHeight = sizeThatFits(size).height
return estimatedHeight
}
var placeholderHeight: CGFloat {
let width = frame.width - (fieldLeftIndent + fieldRightIndent)
let text = placeholderText
let height = text.isEmpty ? 0 : text.height(width: width, font: placeholderFont)
return height
}
var bottomPlaceholderHeight: CGFloat {
let width = frame.width - (fieldLeftIndent + fieldRightIndent)
let text = bottomPlaceholderText
let height = text.isEmpty ? 0 : text.height(width: width, font: bottomPlaceholderFont)
return height
}
func setFrames() {
setPlacehoderFrame()
setBottomElementFrames()
clearImageFrame()
}
var placeholderFrame: CGRect {
let placeholderHeight = self.placeholderHeight
let x = fieldLeftIndent
let y = CGFloat(0)
let width = self.frame.width - (fieldLeftIndent + fieldRightIndent)
let height = placeholderHeight
let frame = CGRect(x: x, y: y, width: width, height: height)
return frame
}
func setPlacehoderFrame() {
placeholderLabel.frame = placeholderFrame
textContainerInset.top = placeholderHeight > 0 ? placeholderHeight + fieldTopIndent : 0
}
func setBottomElementFrames() {
let sepX = CGFloat(0)
let sepY = (frame.height + fieldBottomIndent) - textContainerInset.bottom
let sepWidth = frame.width
let sepHeight = separatorHeight
let sepFrame = CGRect(x: sepX, y: sepY, width: sepWidth, height: sepHeight)
self.separator.frame = sepFrame
let plX = fieldLeftIndent
let plY = sepFrame.maxY + bottomPlaceholderTopIndent
let plWidth = self.frame.width - (fieldLeftIndent + fieldRightIndent)
let plHeight = bottomPlaceholderHeight
let plFrame = CGRect(x: plX, y: plY, width: plWidth, height: plHeight)
bottomPlaceholderLabel.frame = plFrame
let bottomLabelHeight = bottomPlaceholderHeight
let bottomPLaceholderSpace = bottomLabelHeight > 0 ? bottomLabelHeight + bottomPlaceholderTopIndent : 0
let bottomInset = fieldBottomIndent + separatorHeight + bottomPLaceholderSpace
textContainerInset.bottom = bottomInset
}
func clearImageFrame() {
guard withClearButton else {
clearButton.frame = .zero
textContainerInset.right = fieldRightIndent
return
}
let x = frame.width - clearButtonHeight - clearButtonRigthIndent
let textMinY = textContainerInset.top - fieldTopIndent
let textMaxY = frame.height - textContainerInset.bottom + fieldBottomIndent
let y = textMinY + (textMaxY - textMinY) / 2
let frame = CGRect(x: x, y: y, width: clearButtonHeight, height: clearButtonHeight)
clearButton.frame = frame
textContainerInset.right = fieldRightIndent + clearButtonHeight + clearButtonRigthIndent
}
@objc func clearButtonDidTap() {
text = ""
clearButton.isHidden = true
layoutSubviews()
}
}
extension String {
func height(width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
return ceil(boundingBox.height)
}
func height(indent: CGFloat, font: UIFont) -> CGFloat {
let width = UIScreen.main.bounds.width - indent
return height(width: width, font: font)
}
func width(height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
return ceil(boundingBox.width)
}
}