ios - UINavigationController忘记了自定义过渡动画后如何旋转子 Controller

标签 ios objective-c uinavigationcontroller transition ios-animations

我一直在努力解决以下问题。我在文档中找不到我做错的任何事情。我已经通过调试器进行了爬网,并将各种信息都转储到了控制台,但是无法解决所看到的问题。

我的顶级视图控制器是UINavigationController的子类。它包含UIPageViewController的子类,该子类将用于可视化我的数据。它还包含用于设置应用程序设置的UITableViewController的子类。该“选项”视图通过推送功能“链接”到页面视图控制器。至此,一切正常。

当我想区分可视化数据的各种方式(页面视图控制器中的水平滚动)与引入选项视图之间的视觉过渡时,问题就开始了。因此,我创建了一个自定义动画,以将选项控制器从屏幕顶部“拉入”(按入),或者将选项控制器“滚动”到屏幕顶部(弹出)。我使用navigationController:animationControllerFor:operation:from:to:方法在导航控制器上设置此动画。

在显示选项控制器之前,在设备旋转期间一切似乎都工作正常。显示/关闭选项控制器后,数据可视化控制器的布局在旋转过程中会分解。当关闭选项控制器时,它以设备所处的方向正确显示,但以相反的方向错误地播放。

似乎导航控制器(或可能的页面视图控制器)已经忘记了如何处理状态,导航和工具栏的高度。



以下显示的调试屏幕截图说明了5s模拟器上的问题


初始状态。请注意,内容视图(红色)在状态,导航和工具栏下方。


enter image description here


将方向旋转到水平。到目前为止,一切正常。调试控制台显示将视图“旋转”为(568x212)的大小,状态,导航和工具栏的前后高度,屏幕大小,框架矩形和图层位置。我没有在此处显示它,但是图层锚点从(0.5,0.5)不变。


请注意,目标旋转大小是使用以下规则设置的:


rot_width(568)= old_frame_height(460)+ sum_of_bar_heights(108)
rot_height(212)= old_frame_width(320)-sum_of_bar_heights(108)


并使用以下规则设置结果帧大小:


new_frame_width(568)= rot_width(568)
new_frame_height(256)= rot_height(212)+ change_in_bar_height(108-64)


所得到的帧原点(0,64)从屏幕顶部偏移状态栏(20)和导航栏(44)的总和。

enter image description here


将方向旋转回垂直方向。再次,一切都很好。调试控制台为“反向”旋转添加与上述相同的信息。旋转大小和生成的帧大小遵循与上述相同的规则。


enter image description here


使用自定义动画将选项编辑器控制器视图推入导航堆栈。调试控制台为上面的(当前不可见)内容视图添加与上面相同的信息。请注意,没有任何改变。


enter image description here


使用自定义动画将选项编辑器控制器视图弹出导航堆栈。上方的内容视图返回视图。调试控制台添加与上述相同的信息。同样,没有任何变化。


注意,堆叠顺序与显示选项编辑器之前的顺序不同。

使用默认的过渡动画器时,此堆栈重排序不成立(通过从pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted返回nil)

enter image description here


重复第1步。这就是要“变态”的地方。内容视图不再紧贴导航栏和工具栏。调试控制台显示,轮换的所有内容与步骤1中的相同。


但是,轮换的结果与步骤1不同。规则似乎已更改。不再调整生成的框架大小以适应状态,导航和工具栏高度的更改,并且框架原点与旋转之前的原始位置相同。

enter image description here


重复执行步骤2。看来,所有内容都是固定的,但这仅仅是因为它正在按照新的调整大小规则播放。


enter image description here



在此处显示了我的动画师类(完整显示,除了用于产生上面显示的调试信息的探针外):

class OptionsViewAnimator: NSObject, UIViewControllerAnimatedTransitioning
{
  var type : UINavigationControllerOperation

  init(_ type : UINavigationControllerOperation)
  {
    self.type = type
  }

  func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval
  {
    return 0.35
  }

  func animateTransition(using transitionContext: UIViewControllerContextTransitioning)
  {
    if      self.type == .push { showOptions(using:transitionContext) }
    else if self.type == .pop  { hideOptions(using:transitionContext) }
  }

  func showOptions(using context: UIViewControllerContextTransitioning)
  {
    let src       = context.viewController(forKey: .from)!
    let srcView   = context.view(forKey: .from)!
    let dstView   = context.view(forKey: .to)!
    var dstEnd    = context.finalFrame(for: context.viewController(forKey: .to)!)

    let barHeight = (src.navigationController?.toolbar.frame.height) ?? 0.0

    dstEnd.size.height += barHeight

    dstView.frame = dstEnd
    dstView.layer.position = dstEnd.origin
    dstView.layer.anchorPoint = CGPoint(x:0.0,y:0.0)
    dstView.transform = dstView.transform.scaledBy(x: 1.0, y: 0.01)

    UIApplication.shared.keyWindow!.insertSubview(dstView, aboveSubview: srcView)

    UIView.animate(withDuration: 0.35, animations:
      {
        dstView.transform = .identity
      }
    ) {
      (finished)->Void in
      context.completeTransition( !context.transitionWasCancelled )
    }
  }

  func hideOptions(using context: UIViewControllerContextTransitioning)
  {
    let dst       = context.viewController(forKey: .to)!
    let srcView   = context.view(forKey: .from)!
    let dstView   = context.view(forKey: .to)!
    let dstEnd    = context.finalFrame(for: context.viewController(forKey: .to)!)

    dstView.frame = dstEnd

    srcView.layer.position = dstEnd.origin
    srcView.layer.anchorPoint = CGPoint(x:0.0,y:0.0)
    srcView.transform = .identity

    UIApplication.shared.keyWindow!.insertSubview(dstView, belowSubview: srcView)

    UIView.animate(withDuration: 0.35, animations:
      {
        srcView.transform = srcView.transform.scaledBy(x: 1.0, y: 0.01)
      }
    ) {
      (finished)->Void in
      context.completeTransition( !context.transitionWasCancelled )
    }
  }
}


感谢您提供的所有/所有帮助。
麦克风

最佳答案

由于您希望它覆盖UINavigation控制器栏,因此有任何理由不使用自定义模式(在情节提要中以模态形式呈现)。无论哪种方式,我都构建了它以使其与添加窗口无关。在您测试之后,请让我知道,但在我的测试中,两者都可以正常使用。

 import UIKit

class OptionsViewAnimator: NSObject, UIViewControllerAnimatedTransitioning,UIViewControllerTransitioningDelegate
{
  var isPresenting = true
  fileprivate var isNavPushPop = false

  func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval
  {
    return 0.35
  }

  func animateTransition(using transitionContext: UIViewControllerContextTransitioning)
  {
    if isPresenting { showOptions(using:transitionContext) }
    else            { hideOptions(using:transitionContext) }
  }

  func showOptions(using context: UIViewControllerContextTransitioning)
  {
    let src       = context.viewController(forKey: .from)!
    let dstView   = context.view(forKey: .to)!
    let frame = context.finalFrame(for: context.viewController(forKey: .to)!)
    let container = context.containerView
    dstView.frame = frame
    dstView.layer.position = CGPoint(x: container.frame.origin.x, y: container.frame.origin.y + frame.origin.y)
    print("container = \(container.frame)")
    dstView.layer.anchorPoint = CGPoint(x:container.frame.origin.x,y:container.frame.origin.y)
    dstView.transform = dstView.transform.scaledBy(x: 1.0, y: 0.01)
    container.addSubview(dstView)

    UIView.animate(withDuration: 0.35, animations: { dstView.transform = .identity } )
    {
      (finished)->Void in
        src.view.transform = .identity
      context.completeTransition( !context.transitionWasCancelled )
    }
  }

  func hideOptions(using context: UIViewControllerContextTransitioning)
  {
    let srcView   = context.view(forKey: .from)!
    let dstView   = context.view(forKey: .to)!

    let container = context.containerView
    container.insertSubview(dstView, belowSubview: srcView)
    srcView.layer.anchorPoint = CGPoint(x:container.frame.origin.x,y:container.frame.origin.y)
    dstView.frame = context.finalFrame(for: context.viewController(forKey: .to)!)
    dstView.layoutIfNeeded()
    UIView.animate(withDuration: 0.35, animations:
      { srcView.transform = srcView.transform.scaledBy(x: 1.0, y: 0.01) } )
    {
      (finished)->Void in

      context.completeTransition( !context.transitionWasCancelled )
    }
  }

  func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?
  {
    isPresenting = false
    return self
  }


  func animationController(forPresented presented: UIViewController,
                           presenting: UIViewController,
                           source: UIViewController) -> UIViewControllerAnimatedTransitioning?
  {
    isPresenting = true
    return self
  }
}

extension OptionsViewAnimator: UINavigationControllerDelegate
{
  func navigationController(_ navigationController: UINavigationController,
                            animationControllerFor operation: UINavigationControllerOperation,
                            from fromVC: UIViewController,
                            to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?
  {
    isNavPushPop = true
    self.isPresenting = operation == .push
    return self
  }
}


//要用作模式而不是导航,请在下面执行
在视图控制器中声明为属性

    let animator = OptionsViewAnimator()


并准备进行搜索看起来像

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let dvc = segue.destination
    dvc.transitioningDelegate = animator
}


否则推和弹出

self.navigationController?.delegate = animator


或设置为nil

此外,对于导航控制器类中的项目,它应如下所示。

func navigationController(_ navigationController: UINavigationController,
                        animationControllerFor operation:   UINavigationControllerOperation,
                        from fromVC: UIViewController,
                        to toVC: UIViewController
) -> UIViewControllerAnimatedTransitioning?
  {
 var animator : OptionsViewAnimator?

if ( toVC   is OptionsViewController && operation == .push ){
    animator = OptionsViewAnimator()
    animator?.isPresenting = true
}else if ( fromVC is OptionsViewController && operation == .pop ){
    animator = OptionsViewAnimator()
    animator?.isPresenting = false
}
 return animator
}

关于ios - UINavigationController忘记了自定义过渡动画后如何旋转子 Controller ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42479321/

相关文章:

ios - UIDocument closeWithCompletionHandler : completes immediately

objective-c - SKPhysicsBody bodyWithPolygonFromPath 内存泄漏

ios - 为什么我的 UITableViewCells 在我点击它们时会变成灰色?

iphone - 是否可以向系统搜索提供有关我的应用程序的其他信息?

javascript - iphone 的 jplayer 错误

objective-c - 使用 NSOutlineView 作为文件浏览器,从给定目录开始

ios - SKSpriteNode 不稳定滑动?

objective-c - UIAppearance和UINavigationBar Controller

iOS7 UINavigationController pushViewController :animated back to back with animation locks up the main thread

swift - ARSCNView 嵌入导航 Controller 时不显示对象