Present Modally Not Resizing View Properly - ios

I'm trying to present a ViewController (embedded in a NavigationController) from a button inside a TableViewController. The presented ViewController should be half the height of the TableViewController. I've tried with the following code below but it doesn't seem to work (Swift 3). Can someone kindly help? thanks!
class AddNewRecipeTableViewController: UITableViewController, UIViewControllerTransitioningDelegate {
#IBAction func popUpTest(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let pvc = storyboard.instantiateViewController(withIdentifier: "popUpTest") as! UINavigationController
pvc.modalPresentationStyle = UIModalPresentationStyle.custom
pvc.transitioningDelegate = self
self.present(pvc, animated: true, completion: nil)
}
func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController!, sourceViewController source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController: presented, presenting: presentingViewController)
}
}
class HalfSizePresentationController : UIPresentationController {
override var frameOfPresentedViewInContainerView : CGRect {
return CGRect(x: 0, y: 0, width: containerView!.bounds.width, height: containerView!.bounds.height/2)
}
}

You have:
func presentationControllerForPresentedViewController(
presented: UIViewController,
presentingViewController presenting: UIViewController!,
sourceViewController source: UIViewController)
-> UIPresentationController? {
That method will never be called, because in Swift 3 it doesn't correspond to any method that Cocoa knows about. (I'm suprised you don't report getting a warning from the compiler about this.)
You probably meant to implement presentationController(forPresented:presenting:source:), like this:
func presentationController(
forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController)
-> UIPresentationController? {
But even that won't be called, because you have not set the presented view controller's modalPresentationStyle to .custom.

Related

Swift 5 Present ViewController half way

I am presenting a layover VC programmatically.
However, I would like to keep the swipe down functionality of dismissing the VC and keep the background blur while ultimately presenting the VC half way from the bottom of view.
Presenting VC:
let navVC = UINavigationController(rootViewController: SnackBarViewController())
present(navVC, animated: true)
I have found a way to present the VC half way like so, but I lose the swipe functionality and the background blur.
let navVC = UINavigationController(rootViewController: SnackBarViewController())
navVC.transitioningDelegate = self
navVC.modalPresentationStyle = .custom
present(navVC, animated: true)
extension SettingsViewController : UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController: presented, presenting: presenting)
}
}
class HalfSizePresentationController : UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
get {
guard let theView = containerView else {
return CGRect.zero
}
return CGRect(x: 0, y: theView.bounds.height/2, width: theView.bounds.width, height: theView.bounds.height/2)
}
}
}
Is there a better way to present the VC half way in swift 5 without losing the presentation style format by default?
I have also heard of UISheetPresentationController using detents but im not entirely sure how to implement.
Reference here
base ios version 13.0. xcode version 12.5.1

Is there any way to making transition without storyboard?

I tried making transition animation for changing between two viewController. Is there any way to make that without using storyboard?
This code for calling new viewController (which I didn't use storyboard):
#objc func handleAccount() {
let navController = UINavigationController(rootViewController: userButtonLauncher())
navController.transitioningDelegate = self
navController.modalPresentationStyle = .custom
self.present(navController, animated: true, completion: nil)
}
And this codes for transition:
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .present
transition.startingPoint = CGPoint(x: 0, y: 0)
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .pop
transition.startingPoint = CGPoint(x: 0, y: 0)
return transition
}
Of course you can! It depends on what you would like to do, so if you need to use the defaults animation on code without using storyboard you can just:
instantiate your ViewController, set the animation you want and open the ViewController
let vc = self.storyboard?.instantiateViewController(withIdentifier: "vc_id")
vc.modalTransitionStyle = .flipHorizontal
self.present(vc, animated: true, completion: nil)
Otherwise you can create your custom transition, see this easy guide:
https://www.raywenderlich.com/322-custom-uiviewcontroller-transitions-getting-started
I solve my problem. I just add that code for calling animation transition in my homecontroller:
extension HomeController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return faceanim()
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return faceanim()
}
}
And to button action is:
#objc func handleAccount() {
let userConVC = UINavigationController(rootViewController: userButtonLauncher())
userConVC.transitioningDelegate = self
navigationController?.isNavigationBarHidden = false
self.present(userConVC, animated: true, completion: nil)
}

UIViewControllerTransitioningDelegate being deinitialized immediately after it is presented

I'm trying to present a view controller modally with a custom presenter using UIPresentationController (and UIViewControllerTransitioningDelegate).
The problem is that the transitioning delegate is being deinitialized immediately after animationController(presented:presenting:source:) is called. This means animationController(dismissed:) never gets called - and thus, a dismissal animation cannot be set.
In the end, I want to be able to define the dismissal animation. I believe what I explained above is the root of the problem, but can't find anything about this online.
Here is my implementation of UIViewControllerTransitioningDelegate:
final class Manager: NSObject, UIViewControllerTransitioningDelegate {
private let size: CGSize
var animator: Animator
init(size: CGSize) {
self.size = size
self.animator = Animator(duration: 0.4, loaf: loaf)
}
deinit {
print("DEINIT") // 2) Then this is being called immediately after
}
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return Controller(
presentedViewController: presented,
presenting: presenting,
size: size
)
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
animator.presenting = true
return animator // 1) This is called first after the view controller is presented
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
animator.presenting = false
return animator // 3) This is never called
}
}
And this is how I'm setting the transitioning delegate:
extension UIViewController {
func presentModally(_ viewController: UIViewController, size: CGSize) {
viewController.transitioningDelegate = Manager(size: size)
viewController.modalPresentationStyle = .custom
present(viewController, animated: true)
}
}
When the view controller is then dismissed, the view always defaults to being pushed down and disappearing. Again, animationController(dismissed:) is never called and I can't figure out why.
I was able to fix this by storing a reference to the UIViewControllerTransitioningDelegate on the presenting view controller. Then, when presenting the modal, set it like this:
extension UIViewController {
func presentModally(_ viewController: UIViewController, size: CGSize) {
viewController.transDelegate = Manager(size: size)
viewController.transitioningDelegate = viewController.transDelegate
viewController.modalPresentationStyle = .custom
present(viewController, animated: true)
}
}

How to access my custom UIPresentationController from within presented controller?

This is how I perform transitioning:
extension UIViewController: UIViewControllerTransitioningDelegate {
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return OverlayPresentationController(presentedViewController: presented, presenting: presenting)
}
func presentOverlayController(_ controller: UIViewController) {
controller.modalPresentationStyle = .custom
controller.transitioningDelegate = self
present(controller, animated: true)
}
}
And then within my presented controller (AlertVC) at some point I need to access its presentation controller:
print(presentationController as? OverlayPresentationController) //nil
print(presentationController) //is ok, UIPresentationController
Why?
Presenting:
let controller = AlertVC.instantiate()
controller.update()
presentOverlayController(controller)
class AlertVC: UIViewController {
class func instantiate() -> AlertVC {
return UIStoryboard(name: "Alert", bundle: Bundle(for: LoginVC.classForCoder())).instantiateInitialViewController() as! AlertVC
}
func update() {
_ = view
print(presentationController as? OverlayPresentationController) //nil
}
}
You get the view controller that is presenting the current view controller by calling presentingViewController
// The view controller that presented this view controller (or its farthest ancestor.)
self.presentingViewController
If your presenting view controller is in a navigation controller that returns a UINavigationController. You can get the view controller you need like so:
let presentingNVC = self.presentingViewController as? UINavigationViewController
let neededVC = presentingNVC.viewControllers.last as? NeededViewController

How to return two different presentation controllers for two different controllers from extension of UIViewController?

This is my extension:
extension UIViewController: UIViewControllerTransitioningDelegate {
func presentAssignBookToClassesViewController(controller: BWAssignBookToClassesViewController) {
controller.modalPresentationStyle = .Custom
controller.transitioningDelegate = self
controller.preferredContentSize = CGSizeMake(500, 575)
presentViewController(controller, animated: true, completion: nil)
}
func presentSettingsStoryboard() {
if let settingsController = UIStoryboard(name: "TeacherSettingsStoryboard", bundle: nil).instantiateInitialViewController() {
settingsController.modalPresentationStyle = .Custom
settingsController.transitioningDelegate = self
settingsController.preferredContentSize = CGSizeMake(500, 575)
presentViewController(settingsController, animated: true, completion: nil)
}
}
//MARK: - UIViewControllerTransitioningDelegate
public func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? {
return BWOverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
}
Within presentationControllerForPresentedViewController: I need to return either BWOverlayPresentationController or BWSettingsPresentationController depending on what method was called. How to achieve this?
You can simply distinguish them via restorationIdentifier (you can set this simply using storyboard):
//MARK: - UIViewControllerTransitioningDelegate
public func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? {
if presented.restorationIdentifier == BWSettingsRestorationIdentifier {
return BWSettingsPresentationController(presentedViewController: presented, presentingViewController: presenting)
} else {
return BWOverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
}
I would suggest you can create one BaseViewContoller with two viewController objects eg: BWOverlayPresentationController, BWSettingsPresentationController and based on condition you can return the the specific view controller.
public func presentationControllerForPresentedViewController(presented: UIViewController?, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? {
let viewController = BaseViewController()
if (viewController.(somePropertyInViewController)) {
return BWOverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
else {
return BWSettingsPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
public func presentationControllerForPresentedViewController(presented: UIViewController?, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? {
// You can create some property in presented/presenting viewController.
// and check here to return specific viewContoller.
if (presented.(somePropertyInViewController)) {
return BWOverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
else {
return BWSettingsPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
}

Resources