present open modally modal ios swift uiviewcontroller presentmodalviewcontroller

ios - open - swift present modal view controller programmatically



Presentar el controlador de vista modal en el controlador principal de tamaƱo medio (6)

Estoy tratando de presentar el controlador de vista modal en otro controlador de vista del tamaño del controlador de vista medio primario. Pero siempre presente en vista de pantalla completa.

He creado un controlador View de tamaño libre en mi guión gráfico con un tamaño de marco fijo. 320 x 250.

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

He intentado configurar frame.superview y no ayuda.

Por favor aconséjame.


Jannis capturó bien la estrategia general. No funcionó para mí en iOS 9.x con swift 3. En el VC actual, la acción para lanzar el VC presentado es similar a lo que se presentó anteriormente con algunos cambios menores como los siguientes:

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)

Para implementar UIViewControllerTransitioningDelegate en el mismo VC de presentación, la sintaxis es bastante diferente como se destaca en la respuesta SO en https://.com/a/39513247/2886158 . Esta fue la parte más difícil para mí. Aquí está la implementación del protocolo:

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

Para la clase UIPresentationController , tuve que anular la variable frameOfPresentedViewInContainerView , no el método, como se muestra a continuación:

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

Hubo algunas preguntas sobre cómo descartar la vista después de la presentación. Puede implementar toda la lógica habitual en su VC presentada como cualquier otro VC. Implemento una acción para descartar la vista en SomeViewController cuando un usuario SomeViewController pestañas fuera del VC presentado.


Para agregar a la respuesta de Jannis:

En caso de que su vista emergente sea un UIViewController al que AGREGA una tabla en la carga / configuración, deberá asegurarse de que el marco de la tabla que cree coincida con el ancho deseado de la vista real.

Por ejemplo:

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

donde selectedWidth es el ancho que estableció en su clase personalizada (en lo anterior: containerView.bounds.width )

No es necesario imponer nada en la celda, ya que el contenedor de la tabla (al menos en teoría) debe forzar la celda al ancho correcto.


Puede utilizar un UIPresentationController para lograr esto.

Para esto, permite que ViewController presente implemente UIViewControllerTransitioningDelegate y devuelva su PresentationController para la presentación de tamaño medio:

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

Al presentar, configura el estilo de presentación en .Custom y configure su delegado de transición:

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

El controlador de presentación solo devuelve el marco para el controlador de vista presentado:

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

Aquí está el código de trabajo en su totalidad:

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) } }


Sería un arquitecto limpio si presiona algún método delegado de UIViewControllerTransitioningDelegate en su ViewController que desea estar presente en la mitad modal.

Suponiendo que tengamos ViewControllerA presentamos ViewControllerB con medio modal.

en ViewControllerA solo presente ViewControllerB con modalPresentationStyle personalizado

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

Y en 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) }) } } }

El resultado:

Todo el código se publica en mi Github


Solo en caso de que alguien esté buscando hacer esto con Swift 4, como lo estaba yo.

class MyViewController : UIViewController { ... @IBAction func dictionaryButtonTouchUp(_ sender: UIButton) { // Show Apple''s dictionary view. let referenceViewController = UIReferenceLibraryViewController(term: self.textView.currentWord) referenceViewController.view.tintColor = colorLiteral(red: 0.3098039216, green: 0.6745098039, blue: 0.9176470588, alpha: 1) referenceViewController.transitioningDelegate = self referenceViewController.modalPresentationStyle = .custom self.present(referenceViewController, animated: true, completion: nil) } } extension MyViewController : 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) } } }

¡Aclamaciones!


Utilizo la lógica de abajo para presentar media pantalla ViewController

let storyboard = UIStoryboard(name: "Main", bundle: nil) let expVC = storyboard.instantiateViewController(withIdentifier: "AddExperinceVC") as! AddExperinceVC expVC.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext self.present(expVC, animated: true, completion: nil)