Ich habe gerade ein Tutorial erstellt, um ein Modal interaktiv nach unten zu ziehen, um es zu schließen.
http://www.thorntech.com/2016/02/ios-tutorial-close-modal-dragging/
Ich fand dieses Thema zunächst verwirrend, daher wird es im Tutorial Schritt für Schritt erläutert.

Wenn Sie den Code nur selbst ausführen möchten, ist dies das Repo:
https://github.com/ThornTechPublic/InteractiveModal
Dies ist der Ansatz, den ich verwendet habe:
Controller anzeigen
Sie überschreiben die Entlassungsanimation mit einer benutzerdefinierten. Wenn der Benutzer das Modal zieht, wird es interactoraktiviert.
import UIKit
class ViewController: UIViewController {
let interactor = Interactor()
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destinationViewController = segue.destinationViewController as? ModalViewController {
destinationViewController.transitioningDelegate = self
destinationViewController.interactor = interactor
}
}
}
extension ViewController: UIViewControllerTransitioningDelegate {
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return DismissAnimator()
}
func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactor.hasStarted ? interactor : nil
}
}
Animator schließen
Sie erstellen einen benutzerdefinierten Animator. Dies ist eine benutzerdefinierte Animation, die Sie in ein UIViewControllerAnimatedTransitioningProtokoll packen.
import UIKit
class DismissAnimator : NSObject {
}
extension DismissAnimator : UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.6
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
guard
let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey),
let containerView = transitionContext.containerView()
else {
return
}
containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
let screenBounds = UIScreen.mainScreen().bounds
let bottomLeftCorner = CGPoint(x: 0, y: screenBounds.height)
let finalFrame = CGRect(origin: bottomLeftCorner, size: screenBounds.size)
UIView.animateWithDuration(
transitionDuration(transitionContext),
animations: {
fromVC.view.frame = finalFrame
},
completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
}
)
}
}
Interaktor
Sie Unterklasse, UIPercentDrivenInteractiveTransitiondamit es als Ihre Zustandsmaschine fungieren kann. Da beide VCs auf das Interaktorobjekt zugreifen, können Sie damit den Schwenkfortschritt verfolgen.
import UIKit
class Interactor: UIPercentDrivenInteractiveTransition {
var hasStarted = false
var shouldFinish = false
}
Modal View Controller
Dadurch wird der Schwenkgestenstatus Interaktormethodenaufrufen zugeordnet. Der translationInView() yWert bestimmt, ob der Benutzer einen Schwellenwert überschritten hat. Wenn die Schwenkgeste aktiviert ist .Ended, wird der Interaktor entweder beendet oder abgebrochen.
import UIKit
class ModalViewController: UIViewController {
var interactor:Interactor? = nil
@IBAction func close(sender: UIButton) {
dismissViewControllerAnimated(true, completion: nil)
}
@IBAction func handleGesture(sender: UIPanGestureRecognizer) {
let percentThreshold:CGFloat = 0.3
// convert y-position to downward pull progress (percentage)
let translation = sender.translationInView(view)
let verticalMovement = translation.y / view.bounds.height
let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
let downwardMovementPercent = fminf(downwardMovement, 1.0)
let progress = CGFloat(downwardMovementPercent)
guard let interactor = interactor else { return }
switch sender.state {
case .Began:
interactor.hasStarted = true
dismissViewControllerAnimated(true, completion: nil)
case .Changed:
interactor.shouldFinish = progress > percentThreshold
interactor.updateInteractiveTransition(progress)
case .Cancelled:
interactor.hasStarted = false
interactor.cancelInteractiveTransition()
case .Ended:
interactor.hasStarted = false
interactor.shouldFinish
? interactor.finishInteractiveTransition()
: interactor.cancelInteractiveTransition()
default:
break
}
}
}