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

iOS自定义搜索框动画异常:收缩时右边界超出屏幕问题排查

问题原因与修复方案

问题根源

  1. 约束修改时机错误:你在UIView.animate闭包内部激活/禁用宽度约束,这会导致布局引擎在动画执行过程中先瞬时更新约束,再执行动画,从而引发布局计算异常,表现为右边界短暂超出屏幕。
  2. cornerRadius依赖未更新的frame:收缩时你使用self.frame.height计算圆角半径,但此时frame尚未完成动画更新(layoutIfNeeded还未执行),虽然这里高度固定为44,但属于不规范写法,可能在其他场景引发问题。

修复步骤

  1. 将约束的激活/禁用操作移至动画闭包外部,确保约束状态在动画启动前就已确定。
  2. 动画闭包内仅保留布局更新(layoutIfNeeded)和外观属性(边框、圆角)的动画代码。
  3. 保存初始化时传入的高度值,用于收缩时设置圆角半径,避免依赖未更新的frame。

修改后的代码

MySearchBar类修改

public class MySearchBar: UITextField {
    
    private var preAnimationWidth: NSLayoutConstraint?
    private var postAnimationWidth: NSLayoutConstraint?
    private var animationHeight: CGFloat = 0 // 新增:保存初始化高度
        
    public override init(frame: CGRect) {
        super.init(frame: frame)
        
        self.backgroundColor = Theme.GRAY800
        self.borderStyle = .roundedRect
        
        self.layer.masksToBounds = true
        self.clipsToBounds = true
        
        self.autocorrectionType = .no
        
        self.font = FontFamily.ProximaNova.regular.font(size: 16)
        self.textColor = .white
        self.attributedPlaceholder = NSAttributedString(string: "Search", attributes: [.foregroundColor : Theme.GRAY400, .font: FontFamily.ProximaNova.regular.font(size: 16)])
        
        // some further appearance configurations
    }
    
    
    public func setupGrowAnimation(initialWidth: NSLayoutConstraint, grownWidth: NSLayoutConstraint, height: CGFloat) {
        preAnimationWidth = initialWidth
        postAnimationWidth = grownWidth
        animationHeight = height // 保存高度值
        
        self.layer.borderWidth = 0
        self.layer.cornerRadius = height / 2
    }
    
    public func growButton() {
        guard let preAnimationWidth = preAnimationWidth, let postAnimationWidth = postAnimationWidth else { return }
        
        // 约束修改移至动画外部
        preAnimationWidth.isActive = false
        postAnimationWidth.isActive = true
        
        UIView.animate(withDuration: 0.2) {
            self.layer.borderColor = Theme.GRAY600.cgColor
            self.layer.borderWidth = 2
            self.layer.cornerRadius = 8
            
            self.layoutIfNeeded()
        }
    }
    
    public func shrinkButton() {
        guard let preAnimationWidth = preAnimationWidth, let postAnimationWidth = postAnimationWidth else { return }
        
        // 约束修改移至动画外部
        postAnimationWidth.isActive = false
        preAnimationWidth.isActive = true
        
        UIView.animate(withDuration: 0.2) {
            self.layer.borderWidth = 0
            self.layer.borderColor = .none
            self.layer.cornerRadius = self.animationHeight / 2 // 使用保存的高度值
            
            self.layoutIfNeeded()
        }
    }
}

额外验证

初始化代码中的约束设置没有冲突,expandedWidth的计算逻辑(searchBar.widthAnchor.constraint(equalTo: containerView.widthAnchor, constant: -32))对应左右各留16间距(rightAnchor已设为-16,左边自动推导为+16),符合布局预期。

这样修改后,动画过程中约束状态提前确定,布局引擎会基于正确的约束组合执行平滑动画,右边界将保持固定,不会出现超出屏幕的异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 09:55:19