According to the recent project requirements, there is a picture effect similar to the leftView of the label on the left side of the textView and can adapt to the height. Therefore, I customized a textView to achieve the effect, and improved it along the way, and realized the textView to set placeholder characters and modify it. Placeholder character color/font size, setting the maximum number of lines, setting rounded corners and other functions;
1. Realize the effect of the picture on the left
The effect diagram is as follows:
Implementation code:
/// Set the image var hasImage: Bool? A didSet { if (hasImage != nil) && (hasImage == true) { addSubview(imageView) attributedText = imageText(font: UIFont.systemFont(ofSize: fontSize!)) } } } /// Convert the current image to an image as attribute text func imageText(font: UIFont) -> NSAttributedString { // 1. Determine if the image exists guard let image = image else { return NSAttributedString.init(string: "") } // 2. Create a text attachment let attchment = NSTextAttachment() attchment.image = image let height = font.lineHeight attchment.bounds = CGRect(x: 0, y: -4, width: height, height: height) // 3. Return the image attribute text let attrStrM = NSMutableAttributedString(attributedString: NSAttributedString(attachment: attchment)) // set font properties attrStrM.addAttribute(NSAttributedStringKey.font, value: font, range: NSRange(location: 0, length: 1)) // 4. Return the attribute text return attrStrM }
2. Realize adaptive height
Implementation principle: listen for changes in the value of textView, and dynamically calculate the height, if the height is less than the initial value, use the initial value, otherwise use the calculated height block callback to update the height of the textView
Implementation code:
/// The value of textView changes @objc func textDidChanged () { // 1. Whether the placeholder text is hidden placeHolderView.isHidden = (text.lengthOfBytes(using: .utf8) > 0) // 2. Calculate the height var height = CGFloat(ceilf(Float(sizeThatFits(CGSize(width: bounds.size.width , height: CGFloat(MAXFLOAT))).height))) // 2.1 Set the minimum height: initial height height = (height < orginalHeight) ? orginalHeight : height // 3. Fit the height if textHeight != height { // Only fit the height, not limit the number of rows if maxHeight == CGFloat(MAXFLOAT) { isScrollEnabled = false }else { isScrollEnabled = ((height > maxHeight) && (maxHeight > 0)) } textHeight = height if isScrollEnabled == false { blockHeightChanged?(text, height) superview?.layoutIfNeeded() } } }
3. Implement placeholder characters
The principle of placeholder characters is very simple. You can customize a UILabel property in a custom UITextView as the view of placeholder characters.
Show results:
Implementation code:
/// Create a placeholder view func placeholderView() { placeHolderView.frame = bounds placeHolderView.font = font placeHolderView.textColor = UIColor.lightGray placeHolderView.backgroundColor = UIColor.clear addSubview(placeHolderView) } /// placeholder character var placeHolder: String? { didSet{ if let _ = placeHolder { placeholderView() placeHolderView.text = placeHolder } } }
4. Modify the color/font size of placeholder characters
Implementation code:
/// placeholder character color var placeholderColor: UIColor? { didSet{ if let _ = placeHolder { placeHolderView.textColor = placeholderColor } } } /// placeholder character size var placeholderFontSize: CGFloat? { didSet{ if let _ = placeHolder { placeHolderView.font = UIFont.systemFont(ofSize: placeholderFontSize ?? fontSize!) } } }
5. Set the maximum number of rows
It should be noted that if the maximum number of lines is set, before reaching the maximum number of lines, the scrolling of the textView needs to be turned off, that is, isScrollEnabled = false, otherwise during the writing process, the phenomenon of moving up and then falling back may occur.
Implementation code:
/// If the number of lines is 0, just wrap var numOfLines: Int? { didSet { let font = UIFont.systemFont(ofSize: fontSize!) if numOfLines == 0 { maxHeight = CGFloat(MAXFLOAT) return } /// Maximum row height maxHeight = CGFloat(ceilf(Float(font.lineHeight * CGFloat(numOfLines ?? 6) + textContainerInset.top + textContainerInset.bottom))) } }
6. Set rounded corners
Implementation code:
/// Set rounded corners var cornerRadius:CGFloat? { didSet { layer.cornerRadius = cornerRadius ?? 0.0 } }
Finally, the demo address is attached , and you are welcome to point out mistakes.