Swift4下设备旋转后自定义AlertView框架异常的排查与修复
自定义AlertView旋转后显示异常的问题分析与修复方案
我来帮你梳理下这个问题的原因,以及对应的解决办法:
问题根源
你遇到的旋转后高度不足、出现空白的情况,主要有这几个可能的原因:
- 仅修改
frame.size不完整:你的rotateAlert方法只修改了frame.size,但没有设置正确的origin。如果AlertView原本的frame原点不是(0,0),只改size会导致整个视图的位置和大小组合起来无法铺满屏幕。 - 布局时机与动画不同步:在
viewWillTransition里,你在super调用后直接修改frame,但没有参与到系统的旋转过渡动画中,后续系统的布局更新可能会覆盖你的修改。 - AutoLayout的冲突(如果使用了约束):如果你的AlertView或者backgroundView添加了AutoLayout约束,手动修改frame会被系统的布局引擎覆盖——因为AutoLayout会优先根据约束计算并更新frame。
- 未考虑Safe Area:如果你的ViewController的视图包含安全区域(比如刘海屏的顶部/底部),直接使用
self.view.frame.size可能没有排除安全区域的影响,导致实际显示区域和预期不符。
修复方案
方案一:修正Frame布局的写法(适合坚持用frame的场景)
首先修改rotateAlert方法,直接设置完整的全屏frame,而不是只改size:
func rotateAlert(to size: CGSize) { // 直接设置从屏幕原点开始的全屏frame self.frame = CGRect(origin: .zero, size: size) // 让背景视图铺满AlertView的整个bounds(而不是用frame) backgroundView.frame = self.bounds }
然后调整viewWillTransition的代码,把frame更新放到旋转动画的同步块里,确保和系统旋转动画一致,同时去掉不必要的动画禁用代码:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) // 在系统旋转动画的同时更新Alert的布局 coordinator.animate(alongsideTransition: { _ in self.alert?.rotateAlert(to: size) }, completion: nil) }
另外,viewDidLayoutSubviews里的代码可以保留,但建议用self.view.bounds.size代替self.view.frame.size,因为bounds是视图自身的坐标体系,更准确:
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() guard let alert = alert else { return } alert.rotateAlert(to: self.view.bounds.size) }
方案二:改用AutoLayout约束(推荐,更符合iOS布局规范)
手动修改frame在旋转场景下很容易出问题,推荐用AutoLayout让系统自动处理布局:
- 首先在自定义AlertView内部,给backgroundView添加铺满自身的约束:
class CustomAlertView: UIView { let backgroundView: UIView = { let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false // 必须关闭自动转换约束 view.backgroundColor = .black.withAlphaComponent(0.5) return view }() override init(frame: CGRect) { super.init(frame: frame) setupLayout() } required init?(coder: NSCoder) { super.init(coder: coder) setupLayout() } private func setupLayout() { addSubview(backgroundView) // 添加约束让backgroundView完全铺满AlertView NSLayoutConstraint.activate([ backgroundView.topAnchor.constraint(equalTo: topAnchor), backgroundView.leadingAnchor.constraint(equalTo: leadingAnchor), backgroundView.trailingAnchor.constraint(equalTo: trailingAnchor), backgroundView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } }
- 当你把AlertView添加到ViewController的视图上时,同样给AlertView添加铺满父视图的约束:
// 显示Alert的方法示例 func showCustomAlert() { let alert = CustomAlertView() self.view.addSubview(alert) alert.translatesAutoresizingMaskIntoConstraints = false // 让AlertView完全铺满当前ViewController的视图 NSLayoutConstraint.activate([ alert.topAnchor.constraint(equalTo: view.topAnchor), alert.leadingAnchor.constraint(equalTo: view.leadingAnchor), alert.trailingAnchor.constraint(equalTo: view.trailingAnchor), alert.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) self.alert = alert }
这样设置后,不管设备怎么旋转,AutoLayout都会自动调整AlertView的大小,完全不需要手动修改frame,也不会出现显示异常的问题。
内容的提问来源于stack exchange,提问作者Alex Kirov
相关产品推荐
相关产品推荐

