复制UIView到另一页面时如何传递原视图的Frame?
解决复制UIView到其他页面时Frame丢失的问题
问题根源
NSKeyedArchiver/NSKeyedUnarchiver归档复制视图时,不会保留原视图动态计算后的Frame(尤其是原视图用AutoLayout约束时,Frame是布局完成后临时计算的结果,归档仅保存视图属性和约束,不存储最终Frame值)。- 第二个页面的布局逻辑混乱:
- 错误给容器视图
copyView添加自引用约束,完全无效 - 子视图添加顺序颠倒,应先将复制的
viewCpy加入copyView再设置布局 - 多余的
DispatchQueue.main.async和重复调用布局方法
- 错误给容器视图
修复方案
1. 优化视图复制与Frame传递
不依赖归档复制时的Frame,单独将原视图的Frame值传递到目标页面:
修改第一个页面代码:
if let vc = Router.filterPopup.instance as? FilterOptionPopUpVC { vc.hidesBottomBarWhenPushed = true // 复制视图 vc.viewCpy = self.btnSignIn?.copyView() as! UIView // 单独保存原视图的Frame vc.originViewFrame = self.btnSignIn?.frame ?? CGRect() print("原视图Frame:- \(self.btnSignIn?.frame ?? CGRect())") self.navigationController?.present(vc, animated: true) }
在FilterOptionPopUpVC中添加属性:
var viewCpy: UIView! var originViewFrame: CGRect = .zero
2. 修复第二个页面的布局逻辑
根据需求选择直接设置Frame或用AutoLayout约束:
方案A:直接设置Frame(简单快捷)
override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() viewCpy.translatesAutoresizingMaskIntoConstraints = true copyView.addSubview(viewCpy) // 应用原视图的Frame viewCpy.frame = originViewFrame print("复制后视图Frame:- \(viewCpy.frame)") }
方案B:用AutoLayout匹配原Frame的尺寸和位置
override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() viewCpy.translatesAutoresizingMaskIntoConstraints = false copyView.addSubview(viewCpy) NSLayoutConstraint.activate([ // 匹配原Frame的位置 viewCpy.leadingAnchor.constraint(equalTo: copyView.leadingAnchor, constant: originViewFrame.origin.x), viewCpy.topAnchor.constraint(equalTo: copyView.topAnchor, constant: originViewFrame.origin.y), // 匹配原Frame的尺寸 viewCpy.widthAnchor.constraint(equalToConstant: originViewFrame.size.width), viewCpy.heightAnchor.constraint(equalToConstant: originViewFrame.size.height) ]) copyView.layoutIfNeeded() print("复制后视图Frame:- \(viewCpy.frame)") }
3. 可选:改进视图复制方法
避免强制解包风险:
extension UIView { func copyView() -> UIView? { guard let data = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false) else { return nil } return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? UIView } }
额外注意
如果原视图Frame是相对于父视图的,且目标页面容器与原父视图尺寸不同,需转换坐标系统:
// 原页面中转换为屏幕坐标 if let windowFrame = self.btnSignIn?.superview?.convert(self.btnSignIn.frame, to: UIApplication.shared.keyWindow) { vc.originViewFrame = windowFrame } // 目标页面中转换为容器坐标 let targetFrame = self.copyView.convert(originViewFrame, from: UIApplication.shared.keyWindow) viewCpy.frame = targetFrame
内容的提问来源于stack exchange,提问作者steveSarsawa
相关产品推荐
相关产品推荐

