最近项目需求,实现 textView 左侧有一个类似于 label 的 leftView 的图片效果 且 能自适应高度,所以,自定义了一个 textView 实现了效果,顺带着完善了一下,实现了 textView 设置占位字符,修改占位字符颜色/字体大小,设置最大行数、设置圆角等功能;
1、实现左侧图片的效果
效果图如下:
实现代码:
/// 设置图片 var hasImage:Bool? { didSet { if (hasImage != nil) && (hasImage == true) { addSubview(imageView) attributedText = imageText(font: UIFont.systemFont(ofSize: fontSize!)) } } } /// 将当前的图像转换成图片为属性文本 func imageText(font: UIFont) -> NSAttributedString { // 1.判断图像是否存在 guard let image = image else { return NSAttributedString.init(string: "") } // 2.创建文本附件 let attchment = NSTextAttachment() attchment.image = image let height = font.lineHeight attchment.bounds = CGRect(x: 0, y: -4, width: height, height: height) // 3.返回图片属性文本 let attrStrM = NSMutableAttributedString(attributedString: NSAttributedString(attachment: attchment)) // 设置字体属性 attrStrM.addAttribute(NSAttributedStringKey.font, value: font, range: NSRange(location: 0, length: 1)) // 4.返回属性文本 return attrStrM }
2、实现自适应高度
实现原理:监听 textView 的值的改变,并动态计算高度,如果高度小于初始值,使用初始值,否则使用计算的高度 block 回调更新 textView 的高度
实现代码:
/// textView 的值改变 @objc func textDidChanged() { // 1.占位文字是否隐藏 placeHolderView.isHidden = (text.lengthOfBytes(using: .utf8) > 0) // 2.计算高度 var height = CGFloat(ceilf(Float(sizeThatFits(CGSize(width: bounds.size.width , height: CGFloat(MAXFLOAT))).height))) // 2.1 设置最小高度: 初始高度 height = (height < orginalHeight) ? orginalHeight : height // 3.适配高度 if textHeight != height { // 仅适配高度,不限制行数 if maxHeight == CGFloat(MAXFLOAT) { isScrollEnabled = false }else { isScrollEnabled = ((height > maxHeight) && (maxHeight > 0)) } textHeight = height if isScrollEnabled == false { blockHeightChanged?(text, height) superview?.layoutIfNeeded() } } }
3、实现占位字符
占位字符的原理很简单,在自定义的 UITextView 中自定义一个 UILabel的属性作为占位字符的视图即可
效果展示:
实现代码:
/// 创建占位视图 func placeholderView() { placeHolderView.frame = bounds placeHolderView.font = font placeHolderView.textColor = UIColor.lightGray placeHolderView.backgroundColor = UIColor.clear addSubview(placeHolderView) } /// 占位字符 var placeHolder: String? { didSet{ if let _ = placeHolder { placeholderView() placeHolderView.text = placeHolder } } }
4、修改占位字符颜色/字体大小
实现代码:
/// 占位字符颜色 var placeholderColor: UIColor? { didSet{ if let _ = placeHolder { placeHolderView.textColor = placeholderColor } } } /// 占位字符大小 var placeholderFontSize: CGFloat? { didSet{ if let _ = placeHolder { placeHolderView.font = UIFont.systemFont(ofSize: placeholderFontSize ?? fontSize!) } } }
5、设置最大行数
需要注意的是,如果设置了最大行数,在到达最大行数之前,需要将 textView 的滚动关闭,即 isScrollEnabled = false,否则在写的过程中,可能会出现上移然后回落的现象
实现代码:
/// 行数 如果是0,只换行 var numOfLines: Int? { didSet { let font = UIFont.systemFont(ofSize: fontSize!) if numOfLines == 0 { maxHeight = CGFloat(MAXFLOAT) return } /// 最大行高 maxHeight = CGFloat(ceilf(Float(font.lineHeight * CGFloat(numOfLines ?? 6) + textContainerInset.top + textContainerInset.bottom))) } }
6、设置圆角
实现代码:
/// 设置圆角 var cornerRadius:CGFloat? { didSet { layer.cornerRadius = cornerRadius ?? 0.0 } }
最后附上demo地址,欢迎大家指错。