纯代码实现iPad与iPhone差异化UI及下载按钮交互适配咨询
这问题我之前做文件管理类APP时刚好碰到过,纯代码实现跨设备的差异化模态展示其实挺清晰的,核心就是根据设备类型区分逻辑,分别用系统原生的模态样式来实现需求,完全不用碰Storyboard。下面是我验证过的完整实现方案:
1. 先定义要弹出的目标控制器
首先纯代码写好你的Downloads页面,比如:
class DownloadsViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground // 纯代码添加页面内容,这里举个简单例子 let titleLabel = UILabel() titleLabel.text = "我的下载" titleLabel.font = .systemFont(ofSize: 22, weight: .semibold) titleLabel.translatesAutoresizingMaskIntoConstraints = false view.addSubview(titleLabel) NSLayoutConstraint.activate([ titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor) ]) } }
2. iPhone端:底部全屏弹出实现
用iOS13+的pageSheet模态样式,配合sheetPresentationController设置全屏detent,就能实现原生的底部滑入全屏效果:
private func presentForiPhone() { let downloadsVC = DownloadsViewController() downloadsVC.modalPresentationStyle = .pageSheet // 配置sheet的行为 if let sheetController = downloadsVC.sheetPresentationController { // 设置为全屏高度 sheetController.detents = [.large()] // 显示顶部拖拽条(方便用户下拉关闭) sheetController.prefersGrabberVisible = true // 背景完全变暗,不允许点击背景关闭(可选,根据需求调整) sheetController.largestUndimmedDetentIdentifier = .none sheetController.prefersScrollingExpandsWhenScrolledToEdge = false } present(downloadsVC, animated: true) }
3. iPad端:中央方形弹窗实现
用formSheet模态样式,再自定义preferredContentSize来实现居中的方形弹窗,还可以通过代理更精确控制尺寸:
private func presentForiPad() { let downloadsVC = DownloadsViewController() downloadsVC.modalPresentationStyle = .formSheet // 设置方形尺寸,比如400x400 downloadsVC.preferredContentSize = CGSize(width: 400, height: 400) // 如果需要更精准的位置/尺寸控制,实现代理(记得给当前控制器加UIAdaptivePresentationControllerDelegate协议) downloadsVC.presentationController?.delegate = self present(downloadsVC, animated: true) } // 扩展当前控制器实现代理(可选,用于更自定义的弹窗布局) extension YourMainViewController: UIAdaptivePresentationControllerDelegate { func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle { // 强制iPad使用formSheet样式 return .formSheet } func presentationController(_ controller: UIPresentationController, frameOfPresentedViewInContainerView: CGRect) -> CGRect { // 自定义居中的方形frame,适配不同iPad尺寸 let targetSize = CGSize(width: 400, height: 400) let originX = (frameOfPresentedViewInContainerView.width - targetSize.width) / 2 let originY = (frameOfPresentedViewInContainerView.height - targetSize.height) / 2 return CGRect(x: originX, y: originY, width: targetSize.width, height: targetSize.height) } }
4. 按钮点击的逻辑分流
在你的下载按钮点击事件里,判断设备类型,调用对应的展示方法:
// 先纯代码创建按钮(如果还没做的话) private func setupDownloadsButton() { let downloadsButton = UIButton(type: .system) downloadsButton.setTitle("查看下载", for: .normal) downloadsButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) downloadsButton.addTarget(self, action: #selector(downloadsButtonTapped), for: .touchUpInside) downloadsButton.translatesAutoresizingMaskIntoConstraints = false view.addSubview(downloadsButton) // 布局按钮,比如放在导航栏右侧或者页面底部 NSLayoutConstraint.activate([ downloadsButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), downloadsButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24) ]) } // 按钮点击事件 @objc private func downloadsButtonTapped() { if UIDevice.current.userInterfaceIdiom == .pad { presentForiPad() } else { presentForiPhone() } }
额外注意点
- 如果需要兼容iOS14及以下版本,iPhone端的
sheetPresentationController是不存在的,这时候可以自己写一个自定义的底部弹窗:用UIView添加到当前控制器的view上,通过动画改变frame从底部滑入。 - 确保你的主控制器已经遵守了
UIAdaptivePresentationControllerDelegate协议(如果用了iPad的代理方法)。
内容的提问来源于stack exchange,提问作者yakovlevvl




