前言:听说iOS13 把私有的key給禁止了(例如
_placeholderLabel
),乱调会崩溃,我去,当时我就想..........
好了进入正题,之前用
kvc
给UITextView
添加占位文字挺方便的 就是自己在扩展中通过runtime
定义2个属性,自己创建一个label
,最后通过kvc
给_placeholderLabel
就完事了,啥都不要我们操心
那本文就不用
kvc
了设置了,那么占位文字
的显示隐藏就需要自己控制了,咱们通过观察者来实现占位文字
的显示隐藏,真是操碎了心
直接上代码了
extension UITextView {
private struct RuntimeKey {
static let hw_placeholderLabelKey = UnsafeRawPointer.init(bitPattern: "hw_placeholderLabelKey".hashValue)
/// ...其他Key声明
}
/// 占位文字y值调整
@IBInspectable public var placeholderY: CGFloat {
get {
return placeholderLabel.frame.origin.y
}
set {
self.placeholderLabel.frame = CGRect(x: placeholderLabel.frame.origin.x, y: newValue, width: placeholderLabel.frame.size.width, height: placeholderLabel.frame.size.height)
self.setNeedsLayout()
}
}
/// 占位文字
@IBInspectable public var placeholder: String {
get {
return self.placeholderLabel.text ?? ""
}
set {
let attributedText = NSMutableAttributedString(string: newValue)
let style = NSMutableParagraphStyle()
style.lineSpacing = 3 // 别问为啥是3 我也不知道看着像而已
style.lineBreakMode = .byCharWrapping
attributedText.addAttribute(.paragraphStyle, value: style, range: NSMakeRange(0,newValue.count))
placeholderLabel.attributedText = attributedText
}
}
/// 占位文字颜色
@IBInspectable public var placeholderColor: UIColor {
get {
return placeholderLabel.textColor
}
set {
placeholderLabel.textColor = newValue
}
}
private var placeholderLabel: UITextViewPlaceholderLabel {
get {
var label = objc_getAssociatedObject(self, UITextView.RuntimeKey.hw_placeholderLabelKey!) as? UITextViewPlaceholderLabel
if label == nil { // 不存在是 创建 绑定
if (font == nil) { // 防止没大小时显示异常 系统默认设置14
font = UIFont.systemFont(ofSize: 14)
}
let width = bounds.width-10
let y: CGFloat = 8
let x: CGFloat = 6
let frame = CGRect(x: x, y: y, width: width, height: bounds.size.height-y*2) // 别问为啥是这些数字 看着位置对然后就这样了
label = UITextViewPlaceholderLabel.init(frame: frame)
label?.numberOfLines = 0
label?.font = font
label?.textColor = UIColor.lightGray
self.addSubview(label!)
// self.setValue(label!, forKey: "_placeholderLabel") // 听说iOS13 会崩溃
addObserver() // 在此添加观察者 保证只添加一次
objc_setAssociatedObject(self, UITextView.RuntimeKey.hw_placeholderLabelKey!, label!, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
self.sendSubviewToBack(label!)
}
return label!
}
set {
objc_setAssociatedObject(self, UITextView.RuntimeKey.hw_placeholderLabelKey!, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/// 添加观察者监听内容改变
private func addObserver() {
#if swift(>=4.2)
let UITextViewTextDidChange = UITextView.textDidChangeNotification
#else
let UITextViewTextDidChange = Notification.Name.UITextViewTextDidChange
#endif
NotificationCenter.default.addObserver(self, selector: #selector(refreshPlaceholder), name:UITextViewTextDidChange, object: self)
}
@objc internal func refreshPlaceholder() {
if !text.isEmpty || !attributedText.string.isEmpty {
placeholderLabel.alpha = 0
} else {
placeholderLabel.alpha = 1
}
}
open override func removeFromSuperview() {
super.removeFromSuperview()
placeholderLabel.removeFromSuperview() // 移除占位Label
NotificationCenter.default.removeObserver(self) // 移除观察者
}
}
// MARK: - 调整文字内容在左上角
class UITextViewPlaceholderLabel: UILabel {
override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
var textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
textRect.origin.y = bounds.origin.y
return textRect
}
override func drawText(in rect: CGRect) {
let actualRect = textRect(forBounds: rect, limitedToNumberOfLines: numberOfLines)
super.drawText(in: actualRect)
}
}
出现问题:文字大小导致换行,默认位置不满足必须手动设置(图中第三个编辑框为手动设置补正)
针对以上问题新增
placeholderY
手动设置image.png
Dome
欢迎大家一起发现问题探讨解决
网友评论