Xcode 13中UITextView动画切后台返回后为何会覆盖Tab Bar Controller
问题根因
- App退后台再返回前台的场景不会触发
viewWillAppear生命周期方法,你的入场动画不会重新执行;同时系统会在App恢复时重新计算视图布局,手动修改的frame优先级低于Auto Layout约束,会被自动重置,导致位置偏移。 - 硬编码的700偏移量没有适配不同设备的屏幕高度、安全区域,也未处理UITextView滚动区域自动调整的特性,很容易出现越界。
- 你没有限制UITextView的超出裁剪,也没有监听App前后台切换的事件,恢复时没有修正视图位置。
推荐解决方案(基于Auto Layout,稳定性最高)
你原来用frame硬改位置的方式很容易被系统布局冲掉,改为用约束控制位置更稳妥:
- 先在Storyboard/Xib中给
helpText添加约束:左右边距贴合父视图,高度固定(或按内容自适应),初始状态下底部约束等于父视图的底部(即文本视图初始在屏幕外下方),把这个底部约束拖线到控制器中作为属性,命名为helpTextBottomConstraint。 - 调整代码逻辑,同时添加前后台切换监听,代码如下:
import UIKit class InfoViewController: UIViewController { @IBOutlet weak var helpText: UITextView! @IBOutlet weak var helpTextBottomConstraint: NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() // 限制文本内容不超出视图范围,禁止自动调整滚动内边距 helpText.clipsToBounds = true helpText.contentInsetAdjustmentBehavior = .never // 监听App从后台返回前台的事件 NotificationCenter.default.addObserver(self, selector: #selector(resetAndAnimate), name: UIApplication.didBecomeActiveNotification, object: nil) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) resetAndAnimate() } @objc func resetAndAnimate() { // 先重置到初始位置(屏幕外下方) helpTextBottomConstraint.constant = 0 view.layoutIfNeeded() // 执行入场动画,偏移量按你需要的滚动距离调整,也可以动态计算 UIView.animate(withDuration: 4.0, delay: 0, options: [.allowUserInteraction, .curveLinear]) { self.helpTextBottomConstraint.constant = 700 self.view.layoutIfNeeded() } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 动画停止,重置位置 helpText.layer.removeAllAnimations() helpTextBottomConstraint.constant = 0 view.layoutIfNeeded() } deinit { NotificationCenter.default.removeObserver(self) } }
如果你坚持用frame修改的临时方案
如果不想改现有约束逻辑,可以按如下调整:
- 给
helpText和它的父视图都设置clipsToBounds = true,禁止内容超出视图边界。 - 监听前后台切换事件,恢复时重新执行动画逻辑。
- 偏移量改为动态计算,不要硬编码700,适配不同设备。
代码示例:
import UIKit class InfoViewController: UIViewController { @IBOutlet weak var helpText: UITextView! private var originalY: CGFloat = 0 override func viewDidLoad() { super.viewDidLoad() helpText.clipsToBounds = true helpText.superview?.clipsToBounds = true helpText.contentInsetAdjustmentBehavior = .never NotificationCenter.default.addObserver(self, selector: #selector(resetAndAnimate), name: UIApplication.didBecomeActiveNotification, object: nil) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) originalY = helpText.frame.origin.y resetAndAnimate() } @objc func resetAndAnimate() { helpText.frame.origin.y = originalY UIView.animate(withDuration: 4.0, delay: 0, options: [.allowUserInteraction, .curveLinear]) { self.helpText.frame.origin.y -= 700 } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) helpText.layer.removeAllAnimations() helpText.frame.origin.y = originalY } deinit { NotificationCenter.default.removeObserver(self) } }
注意事项
- 硬编码700的偏移量建议改为基于屏幕高度或者文本内容高度动态计算,避免不同设备上动画效果不一致。
- 如果你的UITextView不需要用户手动滚动,可以把
isScrollEnabled设为false,进一步避免滚动偏移问题。 - 如果要适配安全区域,调整约束或者frame时需要把顶部导航栏、底部TabBar的高度计算在内。
内容的提问来源于stack exchange,提问作者Iggy
相关产品推荐
相关产品推荐

