iOS自定义搜索框动画异常:收缩时右边界超出屏幕问题排查
问题原因与修复方案
问题根源
- 约束修改时机错误:你在
UIView.animate闭包内部激活/禁用宽度约束,这会导致布局引擎在动画执行过程中先瞬时更新约束,再执行动画,从而引发布局计算异常,表现为右边界短暂超出屏幕。 - cornerRadius依赖未更新的frame:收缩时你使用
self.frame.height计算圆角半径,但此时frame尚未完成动画更新(layoutIfNeeded还未执行),虽然这里高度固定为44,但属于不规范写法,可能在其他场景引发问题。
修复步骤
- 将约束的激活/禁用操作移至动画闭包外部,确保约束状态在动画启动前就已确定。
- 动画闭包内仅保留布局更新(
layoutIfNeeded)和外观属性(边框、圆角)的动画代码。 - 保存初始化时传入的高度值,用于收缩时设置圆角半径,避免依赖未更新的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
相关产品推荐
相关产品推荐

