Swift代码创建半屏ViewController及上滑全屏实现咨询
嘿,这个交互我之前做项目的时候正好实现过,用UIKit的UIPresentationController搭配拖拽手势就能完美复刻Facebook的这个效果,下面是一步步的代码实现方案,你可以直接拿去用:
实现步骤
1. 自定义PresentationController控制初始半屏状态
首先我们需要一个自定义的UIPresentationController,用来设置弹出ViewController的初始半屏位置,同时添加半透明遮罩层,点击遮罩还能关闭弹窗:
class HalfScreenPresentationController: UIPresentationController { private lazy var dimmingView: UIView = { let view = UIView() view.backgroundColor = UIColor.black.withAlphaComponent(0.5) view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismiss))) return view }() override var frameOfPresentedViewInContainerView: CGRect { guard let containerView = containerView else { return .zero } // 初始占屏幕下半部分,可根据需求调整高度比例 let halfHeight = containerView.bounds.height / 2 return CGRect(x: 0, y: containerView.bounds.height - halfHeight, width: containerView.bounds.width, height: halfHeight) } override func presentationTransitionWillBegin() { super.presentationTransitionWillBegin() dimmingView.frame = containerView?.bounds ?? .zero dimmingView.alpha = 0 containerView?.insertSubview(dimmingView, at: 0) // 遮罩渐显动画 presentedViewController.transitionCoordinator?.animate(alongsideTransition: { _ in self.dimmingView.alpha = 1 }) } override func dismissalTransitionWillBegin() { super.dismissalTransitionWillBegin() // 遮罩渐隐动画 presentedViewController.transitionCoordinator?.animate(alongsideTransition: { _ in self.dimmingView.alpha = 0 }) } @objc private func dismiss() { presentedViewController.dismiss(animated: true) } }
2. 创建支持拖拽的目标ViewController
接下来做我们要弹出的ViewController,给它添加拖拽手势,实现上滑全屏、下滑还原半屏的逻辑,还可以加个顶部小把手提示用户可拖拽:
class DraggableViewController: UIViewController { private var initialY: CGFloat = 0 // 记录初始位置,用于拖拽判断 override func viewDidLoad() { super.viewDidLoad() setupBasicUI() setupPanGesture() } private func setupBasicUI() { view.backgroundColor = .white // 添加顶部拖拽把手 let handleView = UIView() handleView.backgroundColor = .lightGray handleView.layer.cornerRadius = 3 view.addSubview(handleView) handleView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ handleView.widthAnchor.constraint(equalToConstant: 60), handleView.heightAnchor.constraint(equalToConstant: 6), handleView.topAnchor.constraint(equalTo: view.topAnchor, constant: 16), handleView.centerXAnchor.constraint(equalTo: view.centerXAnchor) ]) } private func setupPanGesture() { let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) view.addGestureRecognizer(panGesture) } @objc private func handlePan(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: view) let screenHeight = UIScreen.main.bounds.height switch gesture.state { case .began: initialY = view.frame.origin.y // 记录拖拽前的位置 case .changed: // 限制拖拽范围,不能拖到屏幕顶部以上 let newY = max(initialY + translation.y, view.safeAreaInsets.top) view.frame.origin.y = newY case .ended, .cancelled: let velocity = gesture.velocity(in: view) // 判断触发全屏/还原的条件:上滑速度快 或者 拖拽位置超过屏幕1/4高度 if velocity.y < -300 || view.frame.origin.y < screenHeight / 4 { UIView.animate(withDuration: 0.3) { self.view.frame = UIScreen.main.bounds // 全屏 } } else { UIView.animate(withDuration: 0.3) { self.view.frame.origin.y = screenHeight - (screenHeight / 2) // 回到半屏 } } default: break } } }
3. 在主ViewController中触发弹出
最后在你的主ViewController里,加个按钮点击事件,弹出这个半屏ViewController,并指定我们自定义的PresentationController:
class MainViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground // 创建弹出按钮 let presentButton = UIButton(type: .system) presentButton.setTitle("弹出半屏VC", for: .normal) presentButton.addTarget(self, action: #selector(presentHalfScreenVC), for: .touchUpInside) view.addSubview(presentButton) presentButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ presentButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), presentButton.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) } @objc private func presentHalfScreenVC() { let draggableVC = DraggableViewController() draggableVC.modalPresentationStyle = .custom draggableVC.transitioningDelegate = self present(draggableVC, animated: true) } } // 实现协议,返回自定义的PresentationController extension MainViewController: UIViewControllerTransitioningDelegate { func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { return HalfScreenPresentationController(presentedViewController: presented, presenting: presenting) } }
额外优化提示
- 半屏高度可以根据需求调整,比如把
frameOfPresentedViewInContainerView里的高度改成固定值或者其他比例 - 触发全屏的阈值(比如
screenHeight / 4)可以根据交互体验微调,让触发逻辑更符合用户习惯 - 如果需要支持屏幕旋转,记得在
HalfScreenPresentationController中重写containerViewWillLayoutSubviews()方法,重新计算presentedView的frame
内容的提问来源于stack exchange,提问作者ayjoy
相关产品推荐
相关产品推荐

