2015-03-23 3 views
16

Я пытаюсь представить modal view controller на другом контроллере viewcontroller размером до половины родительского контроллера. Но он всегда присутствует в полноэкранном режиме.Существующий контроллер модального представления в родительском контроллере половинного размера

Я создал свободный размер размером Контроллер в моей раскадровке с фиксированным размером кадра. 320 X 250.

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

Я попытался установить frame.superview, и это не поможет.

Picture example

Пожалуйста, советы.

+0

вы пробовали настройки стиля представления в течение текущего контекста? – boidkan

+0

@boidkan да, я пробовал каждый – Anton

+0

Вы возились с макетом? Как изменение его на обычный, все или компактный. – boidkan

ответ

47

Для этого вы можете использовать UIPresentationController.

Для этого пусть представления ViewController реализовать UIViewControllerTransitioningDelegate и вернуть PresentationController для размера презентации половины:

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

При представлении вы установите стиль презентации в .Custom и установить свои переходящие делегат:

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

Контроллер презентации возвращает рамку только для вашего отображаемого контроллера:

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

Вот рабочий код в полном объеме:

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

Спасибо. он работает – Anton

+3

Спасибо. оно работает. Теперь мне нужно отклонить мой второй контроллер просмотра, когда пользователь нажимает на родительский контроллер представления. Но я не могу получить жестов касания в контроллере родительского представления. Можете ли вы посоветовать мне решить эту проблему? –

+0

Спасибо @Jannis .. btw, я хотел бы поместить круговую кнопку на этот второй контроллер просмотра с 50% высоты кнопки на прозрачном представлении и 50% для вашего примера красной части. Каким будет ваш совет? – Stan92

1

Чтобы добавить ответ Jannis':

В случае, если ваш поп-взгляд на UIViewController, к которому добавить таблицу на нагрузке/setup, вам необходимо убедиться, что созданный вами столбец соответствует требуемой ширине фактического представления.

Например:

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

где chosenWidth является шириной вы установили в пользовательском классе (в приведенном выше: containerView.bounds.width)

Вам не нужно применять что-либо на самой Яке как контейнер таблицы (по крайней мере теоретически) должно заставить ячейку иметь правую ширину.

8

Это был бы чистый архитектор, если вы нажмете какой-то делегатский метод UIViewControllerTransitioningDelegate в вашем ViewController, который хочет присутствовать наполовину модальным.

Предполагая, что у нас есть ViewControllerA настоящее время ViewControllerB с половиной модальных.

в ViewControllerA только настоящим ViewControllerB с пользовательскими modalPresentationStyle

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

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

Результат:

enter image description here

Весь код опубликован на моем Github

3

Jannis хорошо поймал общую стратегию. Это не работает для меня в IOS 9.x с быстрым 3. На предлежащей ВК, действие запустить представленный ВК аналогично тому, что было представлено выше, с некоторыми очень незначительными изменениями, как показано ниже:

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) 

Чтобы реализовать UIViewControllerTransitioningDelegate на том же предъявляющем VC, синтаксис совершенно другой, как указано в ответе SO в https://stackoverflow.com/a/39513247/2886158. Это была самая сложная часть для меня. Вот реализация протокола:

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

Для UIPresentationController класса, я должен был переопределить переменную frameOfPresentedViewInContainerView, а не метод, как показано ниже:

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

Были некоторые вопросы о том, чтобы закрыть вид после презентация. Вы можете реализовать всю обычную логику на своем представленном VC, как и любой другой VC. Я выполняю действие, чтобы отклонить представление в SomeViewController, когда пользователь заходит за пределы представленного VC.

1

Я использую ниже логики представить половину экрана 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)