自定义UITextField的draw(_ rect: CGRect)在iOS14不调用的问题
解决iOS 14中UITextField的draw(_ rect:)不触发问题
嘿,这个问题我之前适配iOS14的时候也碰到过,咱们一步步拆解和解决:
核心问题分析
首先你的代码里有两个关键问题,这也是iOS14不触发draw(_:)的主要原因:
- 把初始化逻辑(添加Layer、设置背景色)放到了
draw(_:)方法里——这个方法是用来做自定义图形绘制的,不是初始化配置的地方。iOS14之后UIKit的绘制优化会跳过没有实际绘制操作的draw(_:)调用。 - 没有调用
super.draw(rect)——系统需要这个调用完成默认的文本绘制流程,缺少它会导致绘制链断裂,draw(_:)可能不被触发。
另外你原代码里的底部边框Layer还有个小bug:边框的height设置成了self.frame.size.height,这会导致边框占满整个TextField高度,而不是只有2pt的底部边框。
修复后的完整代码
class CustomTextField: UITextField { // MARK: - 初始化方法 override init(frame: CGRect) { super.init(frame: frame) commonSetup() } required init?(coder: NSCoder) { super.init(coder: coder) commonSetup() } // MARK: - 初始化配置 private func commonSetup() { // 把初始化逻辑放在这里,而不是draw方法里 backgroundColor = .red // 创建底部边框Layer let borderWidth = CGFloat(2.0) let bottomBorder = CALayer() bottomBorder.name = "bottomBorder" // 标记名称,方便后续更新frame bottomBorder.borderColor = UIColor.black.cgColor // 别忘了设置边框颜色!原代码缺失 bottomBorder.borderWidth = borderWidth bottomBorder.frame = CGRect(x: 0, y: bounds.height - borderWidth, width: bounds.width, height: borderWidth) layer.addSublayer(bottomBorder) layer.masksToBounds = true // 如果需要自定义绘制内容,设置contentMode确保draw被触发 contentMode = .redraw } // MARK: - 自定义绘制 // 只有需要绘制自定义图形(比如特殊样式文字、图标)时才重写这个方法 override func draw(_ rect: CGRect) { super.draw(rect) // 必须调用super的实现! // 这里放真正的绘制代码,比如: // let context = UIGraphicsGetCurrentContext() // context?.setStrokeColor(UIColor.blue.cgColor) // context?.stroke(rect.insetBy(dx: 4, dy: 4)) } // MARK: - 布局更新 // 适配AutoLayout:当视图尺寸变化时更新边框frame override func layoutSubviews() { super.layoutSubviews() guard let bottomBorder = layer.sublayers?.first(where: { $0.name == "bottomBorder" }) else { return } let borderWidth = bottomBorder.borderWidth bottomBorder.frame = CGRect(x: 0, y: bounds.height - borderWidth, width: bounds.width, height: borderWidth) } }
关键修改点说明
- 将初始化逻辑移到
commonSetup():在两个init方法里调用,确保初始化时只执行一次,避免重复添加Layer,也符合UIKit设计规范。 - 修复边框Layer的frame:把边框高度改成
borderWidth,确保只有底部一条2pt的边框。 - 添加
super.draw(rect):保证系统完成默认的文本绘制流程,让draw(_:)方法能正常触发。 - 适配AutoLayout:重写
layoutSubviews()更新边框frame,因为init时的bounds可能还没适配AutoLayout约束。 - 设置
contentMode = .redraw:告诉系统当视图需要更新时必须调用draw(_:)方法,避免iOS14的优化跳过调用。
为什么iOS13正常但iOS14不行?
iOS14之后UIKit对UITextField的绘制流程做了优化:如果系统检测到draw(_:)方法里没有实际的绘制操作(或者执行了不属于绘制的逻辑),就会跳过这个方法的调用以提升性能。而iOS13的绘制逻辑没有这么严格,所以即使代码不规范也能正常运行。
内容的提问来源于stack exchange,提问作者HAMID
相关产品推荐
相关产品推荐

