Cara umum untuk menutup modal adalah dengan menggesek ke bawah - Bagaimana kita mengizinkan pengguna untuk menyeret modal ke bawah, jika cukup jauh, modal ditutup, jika tidak maka bergerak kembali ke posisi semula?
Misalnya, kami dapat menemukan ini digunakan pada tampilan foto aplikasi Twitter, atau mode "temukan" Snapchat.
Utas serupa menunjukkan bahwa kita dapat menggunakan UISwipeGestureRecognizer dan [self dischargeViewControllerAnimated ...] untuk menutup modal VC saat pengguna menggeser ke bawah. Tapi ini hanya menangani satu gesekan, tidak membiarkan pengguna menyeret modal.
ios
viewcontroller
gesture-recognition
foobar.dll
sumber
sumber
Jawaban:
Saya baru saja membuat tutorial untuk menarik modal secara interaktif untuk mengabaikannya.
http://www.thorntech.com/2016/02/ios-tutorial-close-modal-dragging/
Saya menemukan topik ini membingungkan pada awalnya, jadi tutorial menyusun ini langkah demi langkah.
Jika Anda hanya ingin menjalankan kodenya sendiri, ini adalah reponya:
https://github.com/ThornTechPublic/InteractiveModal
Ini adalah pendekatan yang saya gunakan:
Lihat Controller
Anda mengganti animasi penutupan dengan yang khusus. Jika pengguna menyeret modal,
interactor
tendangan masuk.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 } }
Singkirkan Animator
Anda membuat animator khusus. Ini adalah animasi kustom yang Anda paketkan di dalam
UIViewControllerAnimatedTransitioning
protokol.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()) } ) } }
Interactor
Anda membuat subkelas
UIPercentDrivenInteractiveTransition
sehingga dapat bertindak sebagai mesin negara Anda. Karena objek interaktor diakses oleh kedua VC, gunakan itu untuk melacak kemajuan panning.import UIKit class Interactor: UIPercentDrivenInteractiveTransition { var hasStarted = false var shouldFinish = false }
Modal View Controller
Ini memetakan status gerakan geser ke panggilan metode interaksionor. The
translationInView()
y
nilai menentukan apakah pengguna melewati ambang batas. Saat isyarat pan adalah.Ended
, interaksinya akan menyelesaikan atau membatalkan.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 } } }
sumber
segue
untukover current context
menghindari layar hitam di belakang ketika Anda menarik ke bawah viewControllerSaya akan membagikan bagaimana saya melakukannya di Swift 3:
Hasil
Penerapan
class MainViewController: UIViewController { @IBAction func click() { performSegue(withIdentifier: "showModalOne", sender: nil) } }
class ModalOneViewController: ViewControllerPannable { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .yellow } @IBAction func click() { performSegue(withIdentifier: "showModalTwo", sender: nil) } }
class ModalTwoViewController: ViewControllerPannable { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .green } }
Dimana Modals View Controllers mewarisi dari
class
yang telah saya buat (ViewControllerPannable
) untuk membuatnya dapat diseret dan ditutup ketika mencapai kecepatan tertentu.ViewControllerPannable
class ViewControllerPannable: UIViewController { var panGestureRecognizer: UIPanGestureRecognizer? var originalPosition: CGPoint? var currentPositionTouched: CGPoint? override func viewDidLoad() { super.viewDidLoad() panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureAction(_:))) view.addGestureRecognizer(panGestureRecognizer!) } func panGestureAction(_ panGesture: UIPanGestureRecognizer) { let translation = panGesture.translation(in: view) if panGesture.state == .began { originalPosition = view.center currentPositionTouched = panGesture.location(in: view) } else if panGesture.state == .changed { view.frame.origin = CGPoint( x: translation.x, y: translation.y ) } else if panGesture.state == .ended { let velocity = panGesture.velocity(in: view) if velocity.y >= 1500 { UIView.animate(withDuration: 0.2 , animations: { self.view.frame.origin = CGPoint( x: self.view.frame.origin.x, y: self.view.frame.size.height ) }, completion: { (isCompleted) in if isCompleted { self.dismiss(animated: false, completion: nil) } }) } else { UIView.animate(withDuration: 0.2, animations: { self.view.center = self.originalPosition! }) } } } }
sumber
Storyboard segue
dariMainViewController
keModalViewController
: setel properti Presentation ke Over Current ContextBerikut adalah solusi satu file berdasarkan jawaban @ wilson (terima kasih 👍) dengan peningkatan berikut:
Daftar Perbaikan dari solusi sebelumnya
y
koordinatview.frame.origin
let y = max(0, translation.y)
currentPositionTouched
danoriginalPosition
minimumVelocityToHide
: kecepatan apa yang cukup untuk disembunyikan (default ke 1500)minimumScreenRatioToHide
: seberapa rendah cukup untuk disembunyikan (default ke 0,5)animationDuration
: seberapa cepat kita menyembunyikan / menampilkan (default ke 0.2s)Larutan
Swift 3 & Swift 4:
// // PannableViewController.swift // import UIKit class PannableViewController: UIViewController { public var minimumVelocityToHide: CGFloat = 1500 public var minimumScreenRatioToHide: CGFloat = 0.5 public var animationDuration: TimeInterval = 0.2 override func viewDidLoad() { super.viewDidLoad() // Listen for pan gesture let panGesture = UIPanGestureRecognizer(target: self, action: #selector(onPan(_:))) view.addGestureRecognizer(panGesture) } @objc func onPan(_ panGesture: UIPanGestureRecognizer) { func slideViewVerticallyTo(_ y: CGFloat) { self.view.frame.origin = CGPoint(x: 0, y: y) } switch panGesture.state { case .began, .changed: // If pan started or is ongoing then // slide the view to follow the finger let translation = panGesture.translation(in: view) let y = max(0, translation.y) slideViewVerticallyTo(y) case .ended: // If pan ended, decide it we should close or reset the view // based on the final position and the speed of the gesture let translation = panGesture.translation(in: view) let velocity = panGesture.velocity(in: view) let closing = (translation.y > self.view.frame.size.height * minimumScreenRatioToHide) || (velocity.y > minimumVelocityToHide) if closing { UIView.animate(withDuration: animationDuration, animations: { // If closing, animate to the bottom of the view self.slideViewVerticallyTo(self.view.frame.size.height) }, completion: { (isCompleted) in if isCompleted { // Dismiss the view when it dissapeared dismiss(animated: false, completion: nil) } }) } else { // If not closing, reset the view to the top UIView.animate(withDuration: animationDuration, animations: { slideViewVerticallyTo(0) }) } default: // If gesture state is undefined, reset the view to the top UIView.animate(withDuration: animationDuration, animations: { slideViewVerticallyTo(0) }) } } override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nil, bundle: nil) modalPresentationStyle = .overFullScreen; modalTransitionStyle = .coverVertical; } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) modalPresentationStyle = .overFullScreen; modalTransitionStyle = .coverVertical; } }
sumber
minimumScreenRatioToHide
)self.view.frame.origin
sebelum Anda memanggilsliceViewVerticallyTo
pertama kali: tampaknya offset yang kita lihat sama dengan tinggi bilah status, jadi mungkin asal awal Anda bukan 0?slideViewVerticallyTo
sebagai fungsi bertingkat dionPan
.membuat demo untuk menarik ke bawah secara interaktif untuk menutup pengontrol tampilan seperti mode temukan snapchat. Periksa github ini untuk proyek sampel.
sumber
Swift 4.x, Menggunakan Pangesture
Cara yang sederhana
Vertikal
class ViewConrtoller: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(onDrage(_:)))) } @objc func onDrage(_ sender:UIPanGestureRecognizer) { let percentThreshold:CGFloat = 0.3 let translation = sender.translation(in: view) let newX = ensureRange(value: view.frame.minX + translation.x, minimum: 0, maximum: view.frame.maxX) let progress = progressAlongAxis(newX, view.bounds.width) view.frame.origin.x = newX //Move view to new position if sender.state == .ended { let velocity = sender.velocity(in: view) if velocity.x >= 300 || progress > percentThreshold { self.dismiss(animated: true) //Perform dismiss } else { UIView.animate(withDuration: 0.2, animations: { self.view.frame.origin.x = 0 // Revert animation }) } } sender.setTranslation(.zero, in: view) } }
Fungsi pembantu
func progressAlongAxis(_ pointOnAxis: CGFloat, _ axisLength: CGFloat) -> CGFloat { let movementOnAxis = pointOnAxis / axisLength let positiveMovementOnAxis = fmaxf(Float(movementOnAxis), 0.0) let positiveMovementOnAxisPercent = fminf(positiveMovementOnAxis, 1.0) return CGFloat(positiveMovementOnAxisPercent) } func ensureRange<T>(value: T, minimum: T, maximum: T) -> T where T : Comparable { return min(max(value, minimum), maximum) }
Cara yang sulit
Lihat ini -> https://github.com/satishVekariya/DraggableViewController
sumber
modalPresentationStyle = UIModalPresentationOverFullScreen
untuk menghindari layar belakang di belakangview
.Saya menemukan cara super sederhana untuk melakukan ini. Cukup masukkan kode berikut ke pengontrol tampilan Anda:
Cepat 4
override func viewDidLoad() { super.viewDidLoad() let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognizerHandler(_:))) view.addGestureRecognizer(gestureRecognizer) } @IBAction func panGestureRecognizerHandler(_ sender: UIPanGestureRecognizer) { let touchPoint = sender.location(in: view?.window) var initialTouchPoint = CGPoint.zero switch sender.state { case .began: initialTouchPoint = touchPoint case .changed: if touchPoint.y > initialTouchPoint.y { view.frame.origin.y = touchPoint.y - initialTouchPoint.y } case .ended, .cancelled: if touchPoint.y - initialTouchPoint.y > 200 { dismiss(animated: true, completion: nil) } else { UIView.animate(withDuration: 0.2, animations: { self.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height) }) } case .failed, .possible: break } }
sumber
Memperbarui repo Swift 4 secara besar- besaran .
Untuk Swift 3 , saya telah membuat yang berikut ini untuk menampilkan
UIViewController
dari kanan ke kiri dan mengabaikannya dengan gerakan menggeser. Saya telah mengunggah ini sebagai repositori GitHub .DismissOnPanGesture.swift
mengajukan:// Created by David Seek on 11/21/16. // Copyright © 2016 David Seek. All rights reserved. import UIKit class DismissAnimator : NSObject { } extension DismissAnimator : UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.6 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let screenBounds = UIScreen.main.bounds let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) var x:CGFloat = toVC!.view.bounds.origin.x - screenBounds.width let y:CGFloat = toVC!.view.bounds.origin.y let width:CGFloat = toVC!.view.bounds.width let height:CGFloat = toVC!.view.bounds.height var frame:CGRect = CGRect(x: x, y: y, width: width, height: height) toVC?.view.alpha = 0.2 toVC?.view.frame = frame let containerView = transitionContext.containerView containerView.insertSubview(toVC!.view, belowSubview: fromVC!.view) let bottomLeftCorner = CGPoint(x: screenBounds.width, y: 0) let finalFrame = CGRect(origin: bottomLeftCorner, size: screenBounds.size) UIView.animate( withDuration: transitionDuration(using: transitionContext), animations: { fromVC!.view.frame = finalFrame toVC?.view.alpha = 1 x = toVC!.view.bounds.origin.x frame = CGRect(x: x, y: y, width: width, height: height) toVC?.view.frame = frame }, completion: { _ in transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } ) } } class Interactor: UIPercentDrivenInteractiveTransition { var hasStarted = false var shouldFinish = false } let transition: CATransition = CATransition() func presentVCRightToLeft(_ fromVC: UIViewController, _ toVC: UIViewController) { transition.duration = 0.5 transition.type = kCATransitionPush transition.subtype = kCATransitionFromRight fromVC.view.window!.layer.add(transition, forKey: kCATransition) fromVC.present(toVC, animated: false, completion: nil) } func dismissVCLeftToRight(_ vc: UIViewController) { transition.duration = 0.5 transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) transition.type = kCATransitionPush transition.subtype = kCATransitionFromLeft vc.view.window!.layer.add(transition, forKey: nil) vc.dismiss(animated: false, completion: nil) } func instantiatePanGestureRecognizer(_ vc: UIViewController, _ selector: Selector) { var edgeRecognizer: UIScreenEdgePanGestureRecognizer! edgeRecognizer = UIScreenEdgePanGestureRecognizer(target: vc, action: selector) edgeRecognizer.edges = .left vc.view.addGestureRecognizer(edgeRecognizer) } func dismissVCOnPanGesture(_ vc: UIViewController, _ sender: UIScreenEdgePanGestureRecognizer, _ interactor: Interactor) { let percentThreshold:CGFloat = 0.3 let translation = sender.translation(in: vc.view) let fingerMovement = translation.x / vc.view.bounds.width let rightMovement = fmaxf(Float(fingerMovement), 0.0) let rightMovementPercent = fminf(rightMovement, 1.0) let progress = CGFloat(rightMovementPercent) switch sender.state { case .began: interactor.hasStarted = true vc.dismiss(animated: true, completion: nil) case .changed: interactor.shouldFinish = progress > percentThreshold interactor.update(progress) case .cancelled: interactor.hasStarted = false interactor.cancel() case .ended: interactor.hasStarted = false interactor.shouldFinish ? interactor.finish() : interactor.cancel() default: break } }
Penggunaan mudah:
import UIKit class VC1: UIViewController, UIViewControllerTransitioningDelegate { let interactor = Interactor() @IBAction func present(_ sender: Any) { let vc = self.storyboard?.instantiateViewController(withIdentifier: "VC2") as! VC2 vc.transitioningDelegate = self vc.interactor = interactor presentVCRightToLeft(self, vc) } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return DismissAnimator() } func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { return interactor.hasStarted ? interactor : nil } } class VC2: UIViewController { var interactor:Interactor? = nil override func viewDidLoad() { super.viewDidLoad() instantiatePanGestureRecognizer(self, #selector(gesture)) } @IBAction func dismiss(_ sender: Any) { dismissVCLeftToRight(self) } func gesture(_ sender: UIScreenEdgePanGestureRecognizer) { dismissVCOnPanGesture(self, sender, interactor!) } }
sumber
Yang Anda gambarkan adalah animasi transisi kustom interaktif . Anda menyesuaikan animasi dan isyarat mengemudi dari transisi, misalnya menutup (atau tidak) pengontrol tampilan yang disajikan. Cara termudah untuk menerapkannya adalah dengan menggabungkan UIPanGestureRecognizer dengan UIPercentDrivenInteractiveTransition.
Buku saya menjelaskan bagaimana melakukan ini, dan saya telah memposting contoh (dari buku). Contoh khusus ini adalah situasi yang berbeda - transisi ke samping, bukan ke bawah, dan ini untuk pengontrol tab bar, bukan pengontrol yang disajikan - tetapi ide dasarnya persis sama:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch06p296customAnimation2/ch19p620customAnimation1/AppDelegate.swift
Jika Anda mengunduh proyek itu dan menjalankannya, Anda akan melihat bahwa apa yang terjadi persis seperti yang Anda gambarkan, kecuali bahwa itu menyamping: jika seret lebih dari setengah, kami bertransisi, tetapi jika tidak, kami membatalkan dan memasang kembali ke tempat.
sumber
Hanya tutup vertikal
func panGestureAction(_ panGesture: UIPanGestureRecognizer) { let translation = panGesture.translation(in: view) if panGesture.state == .began { originalPosition = view.center currentPositionTouched = panGesture.location(in: view) } else if panGesture.state == .changed { view.frame.origin = CGPoint( x: view.frame.origin.x, y: view.frame.origin.y + translation.y ) panGesture.setTranslation(CGPoint.zero, in: self.view) } else if panGesture.state == .ended { let velocity = panGesture.velocity(in: view) if velocity.y >= 150 { UIView.animate(withDuration: 0.2 , animations: { self.view.frame.origin = CGPoint( x: self.view.frame.origin.x, y: self.view.frame.size.height ) }, completion: { (isCompleted) in if isCompleted { self.dismiss(animated: false, completion: nil) } }) } else { UIView.animate(withDuration: 0.2, animations: { self.view.center = self.originalPosition! }) } }
sumber
Saya telah membuat ekstensi yang mudah digunakan.
Cukup melekatkan UIViewController Anda dengan InteractiveViewController dan Anda sudah selesai InteractiveViewController
panggil metode showInteractive () dari pengontrol Anda untuk ditampilkan sebagai Interaktif.
sumber
Dalam Tujuan C: Inilah kodenya
di
viewDidLoad
UISwipeGestureRecognizer *swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeDown:)]; swipeRecognizer.direction = UISwipeGestureRecognizerDirectionDown; [self.view addGestureRecognizer:swipeRecognizer]; //Swipe Down Method - (void)swipeDown:(UIGestureRecognizer *)sender{ [self dismissViewControllerAnimated:YES completion:nil]; }
sumber
Berikut adalah ekstensi yang saya buat berdasarkan jawaban @Wilson:
// MARK: IMPORT STATEMENTS import UIKit // MARK: EXTENSION extension UIViewController { // MARK: IS SWIPABLE - FUNCTION func isSwipable() { let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) self.view.addGestureRecognizer(panGestureRecognizer) } // MARK: HANDLE PAN GESTURE - FUNCTION @objc func handlePanGesture(_ panGesture: UIPanGestureRecognizer) { let translation = panGesture.translation(in: view) let minX = view.frame.width * 0.135 var originalPosition = CGPoint.zero if panGesture.state == .began { originalPosition = view.center } else if panGesture.state == .changed { view.frame.origin = CGPoint(x: translation.x, y: 0.0) if panGesture.location(in: view).x > minX { view.frame.origin = originalPosition } if view.frame.origin.x <= 0.0 { view.frame.origin.x = 0.0 } } else if panGesture.state == .ended { if view.frame.origin.x >= view.frame.width * 0.5 { UIView.animate(withDuration: 0.2 , animations: { self.view.frame.origin = CGPoint( x: self.view.frame.size.width, y: self.view.frame.origin.y ) }, completion: { (isCompleted) in if isCompleted { self.dismiss(animated: false, completion: nil) } }) } else { UIView.animate(withDuration: 0.2, animations: { self.view.frame.origin = originalPosition }) } } } }
PEMAKAIAN
Di dalam pengontrol tampilan Anda, Anda ingin digesek:
override func viewDidLoad() { super.viewDidLoad() self.isSwipable() }
dan itu akan dapat ditutup dengan menggesek dari sisi paling kiri pengontrol tampilan, sebagai pengontrol navigasi.
sumber
Ini kelas sederhana saya untuk Drag ViewController dari sumbu . Hanya Herited kelas Anda dari DraggableViewController.
MyCustomClass: DraggableViewController
Hanya berfungsi untuk ViewController yang disajikan.
// MARK: - DraggableViewController public class DraggableViewController: UIViewController { public let percentThresholdDismiss: CGFloat = 0.3 public var velocityDismiss: CGFloat = 300 public var axis: NSLayoutConstraint.Axis = .horizontal public var backgroundDismissColor: UIColor = .black { didSet { navigationController?.view.backgroundColor = backgroundDismissColor } } // MARK: LifeCycle override func viewDidLoad() { super.viewDidLoad() view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(onDrag(_:)))) } // MARK: Private methods @objc fileprivate func onDrag(_ sender: UIPanGestureRecognizer) { let translation = sender.translation(in: view) // Movement indication index let movementOnAxis: CGFloat // Move view to new position switch axis { case .vertical: let newY = min(max(view.frame.minY + translation.y, 0), view.frame.maxY) movementOnAxis = newY / view.bounds.height view.frame.origin.y = newY case .horizontal: let newX = min(max(view.frame.minX + translation.x, 0), view.frame.maxX) movementOnAxis = newX / view.bounds.width view.frame.origin.x = newX } let positiveMovementOnAxis = fmaxf(Float(movementOnAxis), 0.0) let positiveMovementOnAxisPercent = fminf(positiveMovementOnAxis, 1.0) let progress = CGFloat(positiveMovementOnAxisPercent) navigationController?.view.backgroundColor = UIColor.black.withAlphaComponent(1 - progress) switch sender.state { case .ended where sender.velocity(in: view).y >= velocityDismiss || progress > percentThresholdDismiss: // After animate, user made the conditions to leave UIView.animate(withDuration: 0.2, animations: { switch self.axis { case .vertical: self.view.frame.origin.y = self.view.bounds.height case .horizontal: self.view.frame.origin.x = self.view.bounds.width } self.navigationController?.view.backgroundColor = UIColor.black.withAlphaComponent(0) }, completion: { finish in self.dismiss(animated: true) //Perform dismiss }) case .ended: // Revert animation UIView.animate(withDuration: 0.2, animations: { switch self.axis { case .vertical: self.view.frame.origin.y = 0 case .horizontal: self.view.frame.origin.x = 0 } }) default: break } sender.setTranslation(.zero, in: view) } }
sumber
Bagi mereka yang benar-benar ingin menyelami lebih dalam Transisi UIViewController Kustom, saya merekomendasikan tutorial hebat ini dari raywenderlich.com .
Proyek sampel akhir asli mengandung bug. Jadi saya memperbaikinya dan mengunggahnya ke repo Github . Projnya ada di Swift 5, jadi Anda dapat dengan mudah menjalankan dan memainkannya.
Berikut pratinjau:
Dan itu juga interaktif!
Selamat meretas!
sumber
Anda dapat menggunakan UIPanGestureRecognizer untuk mendeteksi seret pengguna dan memindahkan tampilan modal dengannya. Jika posisi akhir cukup jauh di bawah, tampilan dapat ditutup, atau dianimasikan kembali ke posisi semula.
Lihat jawaban ini untuk informasi lebih lanjut tentang bagaimana menerapkan sesuatu seperti ini.
sumber