Contrôleur de vue modale présent dans le controller parent demi-taille

J'essaye de présenter le controller de vue modale sur l'autre controller de viewcontroller classé au controller de vue de demi-parent. Mais il est toujours présent en plein écran.

J'ai créé un controller View de taille libre dans mon storyboard avec une taille d'image fixe. 320 X 250.

var storyboard = UIStoryboard(name: "Main", bundle: nil) var pvc = storyboard.instantiateViewControllerWithIdentifier("CustomTableViewController") as ProductsTableViewController self.presentViewController(pvc, animated: true, completion: nil) 

J'ai essayé de mettre frame.superview et cela n'aide pas.

Exemple d'image

Veuillez nous conseiller

Vous pouvez utiliser un UIPresentationController pour y parvenir.

Pour cela, vous laissez le ViewController présentateur implémenter UIViewControllerTransitioningDelegate et returnner votre PresentationController pour la présentation demi-taille:

 func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController!, sourceViewController source: UIViewController) -> UIPresentationController? { return HalfSizePresentationController(presentedViewController: presented, presentingViewController: presenting) } 

Lors de la présentation, définissez le style de présentation sur .Custom et définissez votre délégué de transition:

 pvc.modalPresentationStyle = UIModalPresentationStyle.Custom pvc.transitioningDelegate = self 

Le controller de présentation ne renvoie que le cadre de votre controller de vue présenté:

 class HalfSizePresentationController : UIPresentationController { override func frameOfPresentedViewInContainerView() -> CGRect { return CGRect(x: 0, y: 0, width: containerView.bounds.width, height: containerView.bounds.height/2) } } 

Voici le code de travail dans son intégralité:

 class ViewController: UIViewController, UIViewControllerTransitioningDelegate { @IBAction func tap(sender: AnyObject) { var storyboard = UIStoryboard(name: "Main", bundle: nil) var pvc = storyboard.instantiateViewControllerWithIdentifier("CustomTableViewController") as UITableViewController pvc.modalPresentationStyle = UIModalPresentationStyle.Custom pvc.transitioningDelegate = self pvc.view.backgroundColor = UIColor.redColor() self.presentViewController(pvc, animated: true, completion: nil) } func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController!, sourceViewController source: UIViewController) -> UIPresentationController? { return HalfSizePresentationController(presentedViewController: presented, presentingViewController: presentingViewController) } } class HalfSizePresentationController : UIPresentationController { override func frameOfPresentedViewInContainerView() -> CGRect { return CGRect(x: 0, y: 0, width: containerView.bounds.width, height: containerView.bounds.height/2) } } 

Ce serait un architecte propre si vous poussez une méthode déléguée de UIViewControllerTransitioningDelegate dans votre ViewController qui veut être présent à moitié modal.

En supposant que nous ayons ViewControllerA présent ViewControllerB avec demi modal.

dans ViewControllerA présente ViewControllerB avec modalPresentationStyle personnalisé

 func gotoVCB(_ sender: UIButton) { let vc = ViewControllerB() vc.modalPresentationStyle = .custom present(vc, animated: true, completion: nil) } 

Et dans ViewControllerB:

 import UIKit final class ViewControllerB: UIViewController { lazy var backdropView: UIView = { let bdView = UIView(frame: self.view.bounds) bdView.backgroundColor = UIColor.black.withAlphaComponent(0.5) return bdView }() let menuView = UIView() let menuHeight = UIScreen.main.bounds.height / 2 var isPresenting = false init() { super.init(nibName: nil, bundle: nil) modalPresentationStyle = .custom transitioningDelegate = self } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .clear view.addSubview(backdropView) view.addSubview(menuView) menuView.backgroundColor = .red menuView.translatesAutoresizingMaskIntoConstraints = false menuView.heightAnchor.constraint(equalToConstant: menuHeight).isActive = true menuView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true menuView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true menuView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewControllerB.handleTap(_:))) backdropView.addGestureRecognizer(tapGesture) } func handleTap(_ sender: UITapGestureRecognizer) { dismiss(animated: true, completion: nil) } } extension ViewControllerB: UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return self } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return self } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 1 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let containerView = transitionContext.containerView let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) guard let toVC = toViewController else { return } isPresenting = !isPresenting if isPresenting == true { containerView.addSubview(toVC.view) menuView.frame.origin.y += menuHeight backdropView.alpha = 0 UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: { self.menuView.frame.origin.y -= self.menuHeight self.backdropView.alpha = 1 }, completion: { (finished) in transitionContext.completeTransition(true) }) } else { UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: { self.menuView.frame.origin.y += self.menuHeight self.backdropView.alpha = 0 }, completion: { (finished) in transitionContext.completeTransition(true) }) } } } 

Le résultat:

entrez la description de l'image ici

Tout le code est publié sur mon Github

Pour append à la réponse de Jannis:

Dans le cas où votre pop-view est un UIViewController auquel vous ajoutez une table lors du chargement / de l'installation, vous devez vous assurer que l'image de table que vous créez correspond à la largeur souhaitée de la vue réelle.

Par exemple:

 let tableFrame: CGRect = CGRectMake(0, 0, chosenWidth, CGFloat(numOfRows) * rowHeight) 

chosenWidth est la largeur que vous définissez dans votre class personnalisée (dans ce qui précède: containerView.bounds.width )

Vous n'avez pas besoin d'appliquer quoi que ce soit sur la cellule car le conteneur de table (au less en théorie) doit forcer la cellule à la bonne largeur.

Jannis a bien saisi la stratégie globale. Il n'a pas fonctionné pour moi dans iOS 9.x avec swift 3. Sur le VC de présentation, l'action pour lancer le VC présenté est similaire à ce qui a été présenté ci-dessus avec quelques changements très mineurs comme ci-dessous:

 let storyboard = UIStoryboard(name: "Main", bundle: nil) let pvc = storyboard.instantiateViewController(withIdentifier: "SomeScreen") as SomeViewController pvc.modalPresentationStyle = .custom pvc.transitioningDelegate = self present(pvc, animated: true, completion: nil) 

Pour implémenter UIViewControllerTransitioningDelegate sur le même VC présentateur, la syntaxe est très différente, comme souligné dans la réponse SO dans https://stackoverflow.com/a/39513247/2886158 . C'est la partie la plus difficile pour moi. Voici l'implémentation du protocole:

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

Pour la class UIPresentationController , j'ai dû redéfinir la variable frameOfPresentedViewInContainerView , pas la méthode, comme ci-dessous:

 class HalfSizePresentationController: UIPresentationController { override var frameOfPresentedViewInContainerView: CGRect { return CGRect(x: 0, y: 0, width: containerView!.bounds.width, height: containerView!.bounds.height/2) } } 

Il y avait quelques questions sur la façon de rejeter la vue après la présentation. Vous pouvez implémenter toute la logique habituelle sur votre VC présenté comme n'importe quel autre VC. Je implémente une action pour SomeViewController la vue dans SomeViewController quand un user SomeViewController en dehors du VC présenté.