You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义UITextField的draw(_ rect: CGRect)在iOS14不调用的问题

解决iOS 14中UITextField的draw(_ rect:)不触发问题

嘿,这个问题我之前适配iOS14的时候也碰到过,咱们一步步拆解和解决:

核心问题分析

首先你的代码里有两个关键问题,这也是iOS14不触发draw(_:)的主要原因:

  1. 把初始化逻辑(添加Layer、设置背景色)放到了draw(_:)方法里——这个方法是用来做自定义图形绘制的,不是初始化配置的地方。iOS14之后UIKit的绘制优化会跳过没有实际绘制操作的draw(_:)调用。
  2. 没有调用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)
    }
}

关键修改点说明

  1. 将初始化逻辑移到commonSetup():在两个init方法里调用,确保初始化时只执行一次,避免重复添加Layer,也符合UIKit设计规范。
  2. 修复边框Layer的frame:把边框高度改成borderWidth,确保只有底部一条2pt的边框。
  3. 添加super.draw(rect):保证系统完成默认的文本绘制流程,让draw(_:)方法能正常触发。
  4. 适配AutoLayout:重写layoutSubviews()更新边框frame,因为init时的bounds可能还没适配AutoLayout约束。
  5. 设置contentMode = .redraw:告诉系统当视图需要更新时必须调用draw(_:)方法,避免iOS14的优化跳过调用。

为什么iOS13正常但iOS14不行?

iOS14之后UIKit对UITextField的绘制流程做了优化:如果系统检测到draw(_:)方法里没有实际的绘制操作(或者执行了不属于绘制的逻辑),就会跳过这个方法的调用以提升性能。而iOS13的绘制逻辑没有这么严格,所以即使代码不规范也能正常运行。

内容的提问来源于stack exchange,提问作者HAMID

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 19:02:55