gpt4 book ai didi

ios - 自定义交互过渡动画

转载 作者:搜寻专家 更新时间:2023-10-30 22:01:10 25 4
gpt4 key购买 nike

我想在两个 View Controller 之间实现交互式转换。我希望它是模态或现在的过渡。

  • 我希望应用程序从第一个 View Controller 开始,并允许用户向下滑动以引入第二个 View Controller
  • 第二个 View Controller 应该进入并覆盖当前(第一个 View Controller )而不是将其移开

我知道我需要使用以下内容。

transitioningDelegate

animationController(forPresented:presenting:Source:)

interactionControllerForPresentation(Using:)

UIPercentDrivenInteractiveTransition

我无法弄清楚如何实现所有这些。我似乎在 swift 3 中找不到任何有用的东西或任何工作示例。现在我创建了一个简单的单 View 应用程序,它有两个 View Controller VC1(蓝色背景)和 VC2(黄色背景)来轻松测试任何可能的解决方案。

最佳答案

参见 WWDC 2013 视频 Custom Transitions Using View Controllers用于讨论过渡委托(delegate)、动画 Controller 和交互 Controller 。查看 WWDC 2014 视频 View Controller Advancements in iOS 8A Look Inside Presentation Controllers用于介绍演示 Controller (您也应该使用它)。

基本思想是创建一个转换委托(delegate)对象,该对象标识将用于自定义转换的动画 Controller 、交互 Controller 和演示 Controller :

class TransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {

/// Interaction controller
///
/// If gesture triggers transition, it will set will manage its own
/// `UIPercentDrivenInteractiveTransition`, but it must set this
/// reference to that interaction controller here, so that this
/// knows whether it's interactive or not.

weak var interactionController: UIPercentDrivenInteractiveTransition?

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return PullDownAnimationController(transitionType: .presenting)
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return PullDownAnimationController(transitionType: .dismissing)
}

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

func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactionController
}

func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactionController
}

}

然后您只需指定正在使用的自定义转换以及应使用的转换委托(delegate)。您可以在实例化目标 View Controller 时执行此操作,或者您可以将其指定为目标 View Controller 的 init 的一部分,例如:

class SecondViewController: UIViewController {

let customTransitionDelegate = TransitioningDelegate()

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

modalPresentationStyle = .custom
transitioningDelegate = customTransitionDelegate
}

...
}

动画 Controller 指定动画的细节(如何制作动画、用于非交互式过渡的持续时间等):

class PullDownAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

enum TransitionType {
case presenting
case dismissing
}

let transitionType: TransitionType

init(transitionType: TransitionType) {
self.transitionType = transitionType

super.init()
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let inView = transitionContext.containerView
let toView = transitionContext.view(forKey: .to)!
let fromView = transitionContext.view(forKey: .from)!

var frame = inView.bounds

switch transitionType {
case .presenting:
frame.origin.y = -frame.size.height
toView.frame = frame

inView.addSubview(toView)
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
toView.frame = inView.bounds
}, completion: { finished in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
case .dismissing:
toView.frame = frame
inView.insertSubview(toView, belowSubview: fromView)

UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
frame.origin.y = -frame.size.height
fromView.frame = frame
}, completion: { finished in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}

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

上面的动画 Controller 处理呈现和解散,但如果感觉太复杂,理论上你可以将它分成两个类,一个用于呈现,另一个用于解散。但我不喜欢两个不同的类如此紧密地耦合在一起,所以我将承担 animateTransition 的轻微复杂性的代价,以确保它们都很好地封装在一个类中。

无论如何,我们想要的下一个对象是展示 Controller 。在这种情况下,表示 Controller 告诉我们从 View 层次结构中删除原始 View Controller 的 View 。 (在这种情况下,我们这样做是因为您要过渡到的场景恰好占据了整个屏幕,因此无需在 View 层次结构中保留旧 View 。)如果您要添加任何其他额外的镶边(例如添加调光/模糊 View 等),它们将属于呈现 Controller 。

无论如何,在这种情况下,呈现 Controller 非常简单:

class PresentationController: UIPresentationController {
override var shouldRemovePresentersView: Bool { return true }
}

最后,您可能想要一个手势识别器:

  • 实例化UIPercentDrivenInteractiveTransition
  • 自己启动转换;
  • 随着手势的进行更新 UIPercentDrivenInteractiveTransition
  • 在手势完成时取消或结束交互过渡;和
  • 在完成后删除 UIPercentDrivenInteractiveTransition(以确保它不会挥之不去,因此它不会干扰您稍后可能想做的任何非交互式转换......这是一个微妙的小点容易被忽视)。

所以“呈现” View Controller 可能有一个手势识别器,它可能会做类似的事情:

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let panDown = UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:)))
view.addGestureRecognizer(panDown)
}

var interactionController: UIPercentDrivenInteractiveTransition?

// pan down transitions to next view controller

func handleGesture(_ gesture: UIPanGestureRecognizer) {
let translate = gesture.translation(in: gesture.view)
let percent = translate.y / gesture.view!.bounds.size.height

if gesture.state == .began {
let controller = storyboard!.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
interactionController = UIPercentDrivenInteractiveTransition()
controller.customTransitionDelegate.interactionController = interactionController

show(controller, sender: self)
} else if gesture.state == .changed {
interactionController?.update(percent)
} else if gesture.state == .ended || gesture.state == .cancelled {
let velocity = gesture.velocity(in: gesture.view)
if (percent > 0.5 && velocity.y == 0) || velocity.y > 0 {
interactionController?.finish()
} else {
interactionController?.cancel()
}
interactionController = nil
}
}

}

您可能还想更改此设置,使其仅识别向下的手势(而不是任何旧的平底锅),但希望这能说明这个想法。

并且您可能希望“呈现的” View Controller 具有用于关闭场景的手势识别器:

class SecondViewController: UIViewController {

let customTransitionDelegate = TransitioningDelegate()

required init?(coder aDecoder: NSCoder) {
// as shown above
}

override func viewDidLoad() {
super.viewDidLoad()

let panUp = UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:)))
view.addGestureRecognizer(panUp)
}

// pan up transitions back to the presenting view controller

var interactionController: UIPercentDrivenInteractiveTransition?

func handleGesture(_ gesture: UIPanGestureRecognizer) {
let translate = gesture.translation(in: gesture.view)
let percent = -translate.y / gesture.view!.bounds.size.height

if gesture.state == .began {
interactionController = UIPercentDrivenInteractiveTransition()
customTransitionDelegate.interactionController = interactionController

dismiss(animated: true)
} else if gesture.state == .changed {
interactionController?.update(percent)
} else if gesture.state == .ended {
let velocity = gesture.velocity(in: gesture.view)
if (percent > 0.5 && velocity.y == 0) || velocity.y < 0 {
interactionController?.finish()
} else {
interactionController?.cancel()
}
interactionController = nil
}

}

@IBAction func didTapButton(_ sender: UIButton) {
dismiss(animated: true)
}

}

参见 https://github.com/robertmryan/SwiftCustomTransitions用于演示上述代码。

看起来像:

interactive gesture demo

但是,归根结底,自定义转场有点复杂,所以我再次向您推荐那些原始视频。在发布任何进一步的问题之前,请确保您仔细观察了它们。您的大部分问题可能会在这些视频中得到解答。

关于ios - 自定义交互过渡动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42192127/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com