如何使用iOS 7自定义转场,在页面上呈现一个半模态视图控制器

14

如何在主视图控制器的顶部呈现“半视图”控制器?

要求: - 呈现第二个视图控制器,该控制器从主视图控制器的顶部滑入。 - 第二个视图控制器仅应显示在主视图控制器的一半上方。 - 主视图控制器应保持在第二个视图控制器后面可见(透明背景,不显示黑色底部)。 - 第二个视图控制器应以类似于模态垂直覆盖或 iOS 7 自定义转换的动画方式进入。 - 用户可以在第二个视图控制器处于活动状态时仍与主视图控制器上的按钮交互(即第二个视图控制器未完全覆盖主视图控制器)。 - 第二个视图控制器具有其自己的复杂逻辑(不能是简单的视图)。 - 使用故事板、Segue 和 iOS 7。 - 仅限 iPhone,不包括 iPad。

我尝试使用模态视图控制器,但这不允许与主视图控制器进行交互。有人能否提供一个使用 iOS7 自定义转换或其他方法来实现此功能的示例。


看一下 UIActionSheet,那可能解决你的问题。这是一个链接,你可以从这里开始:http://eureka.ykyuen.info/2010/04/14/iphone-uiactionsheet-example/ - Syed Shoaib Abidi
2个回答

26

一种方法是将"半模态"控制器作为子视图控制器添加,并对其视图执行动画以使其进入正确的位置。对于此示例,我在故事板中创建了一个"半模态"控制器,其框架高度为4英寸iPhone屏幕的一半。您可以使用更动态的方法来适应不同的屏幕尺寸,但这应该可以帮助您入门。

@interface ViewController ()
@property (strong,nonatomic) UIViewController *modal;
@end

@implementation ViewController


- (IBAction)toggleHalfModal:(UIButton *)sender {
    if (self.childViewControllers.count == 0) {
        self.modal = [self.storyboard instantiateViewControllerWithIdentifier:@"HalfModal"];
        [self addChildViewController:self.modal];
        self.modal.view.frame = CGRectMake(0, 568, 320, 284);
        [self.view addSubview:self.modal.view];
        [UIView animateWithDuration:1 animations:^{
            self.modal.view.frame = CGRectMake(0, 284, 320, 284);;
        } completion:^(BOOL finished) {
            [self.modal didMoveToParentViewController:self];
        }];
    }else{
        [UIView animateWithDuration:1 animations:^{
            self.modal.view.frame = CGRectMake(0, 568, 320, 284);
        } completion:^(BOOL finished) {
            [self.modal.view removeFromSuperview];
            [self.modal removeFromParentViewController];
            self.modal = nil;
        }];
    }
}

1
这个很好用。是个好的解决方案。从模态视图控制器内部解除该模态视图的最佳方法是什么?(不是从主视图控制器切换) - johnsampson
@johnsampson,你可以使用self.parentViewController获取主视图控制器的指针,并使用它调用toggleHalfModal。 - rdelmar
抱歉,我是一个真正的初学者。多亏了你的代码,我已经让模态框运作起来了。我该如何使用self.parentViewController从模态框中调用toggleHalfModal函数? - johnsampson
1
@johnsampson,调用任何方法的方式都是一样的--[objectToSendMessageTo message]; 在这种情况下,您想要发送消息的对象是self.parentViewController(您需要进行强制转换,以便编译器知道它是什么,并导入其.h文件)。所以这给了你[(MainViewController *)self.parentViewController toggleHalfModal]; 将MainViewController替换为该控制器的类名。 - rdelmar
2
@Septronic,一种方法是在动画显示新视图之前,在原始视图上方添加一个半透明(黑色并具有低 alpha 值)视图。 - rdelmar
显示剩余4条评论

-1

在 iOS 中,显示控制器的新方法是使用原生样式的 controller.sheetPresentationController 来实现半屏幕显示。但这仅适用于 iOS 15 及更高版本,对于之前的版本,您需要设置 .custom modalPresentationStype。

下面的代码片段可以帮助您同时处理两个版本。

    controller.modalPresentationStyle = .pageSheet
    if #available(iOS 15.0, *) {

        if let sheet = controller.sheetPresentationController {
            sheet.detents = [.medium()]
        }
        
    } else {
        
        controller.modalPresentationStyle = .custom
        controller.transitioningDelegate = self
    }
    self.present(controller, animated: true, completion: nil)

// 标记: - UIViewControllerTransitioningDelegate

  extension CPPdfPreviewVC: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    PresentationController(presentedViewController: presented, presenting: presenting)
    }
}

并按照给定的方式添加演示控制器

  class PresentationController: UIPresentationController {


  let blurEffectView: UIVisualEffectView!
  var tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer()
  
  override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
      let blurEffect = UIBlurEffect(style: .dark)
      blurEffectView = UIVisualEffectView(effect: blurEffect)
      super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
      tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissController))
      blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
      self.blurEffectView.isUserInteractionEnabled = true
      self.blurEffectView.addGestureRecognizer(tapGestureRecognizer)
  }
  
  override var frameOfPresentedViewInContainerView: CGRect {
      CGRect(origin: CGPoint(x: 0, y: self.containerView!.frame.height * 0.4),
             size: CGSize(width: self.containerView!.frame.width, height: self.containerView!.frame.height *
              0.6))
  }

  override func presentationTransitionWillBegin() {
      self.blurEffectView.alpha = 0
      self.containerView?.addSubview(blurEffectView)
      self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
          self.blurEffectView.alpha = 0.7
      }, completion: { (UIViewControllerTransitionCoordinatorContext) in })
  }
  
  override func dismissalTransitionWillBegin() {
      self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
          self.blurEffectView.alpha = 0
      }, completion: { (UIViewControllerTransitionCoordinatorContext) in
          self.blurEffectView.removeFromSuperview()
      })
  }
  
  override func containerViewWillLayoutSubviews() {
      super.containerViewWillLayoutSubviews()
    presentedView!.roundCorners([.topLeft, .topRight], radius: 22)
  }

  override func containerViewDidLayoutSubviews() {
      super.containerViewDidLayoutSubviews()
      presentedView?.frame = frameOfPresentedViewInContainerView
      blurEffectView.frame = containerView!.bounds
  }

  @objc func dismissController(){
      self.presentedViewController.dismiss(animated: true, completion: nil)
  }
}

extension UIView {
  func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
      let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners,
                              cornerRadii: CGSize(width: radius, height: radius))
      let mask = CAShapeLayer()
      mask.path = path.cgPath
      layer.mask = mask
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接