Существующий контроллер модального представления в половинном родительском контроллере
Я пытаюсь представить 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]()
Пожалуйста, совет.
Ответы
Ответ 1
Для достижения этой цели вы можете использовать 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)
}
}
Ответ 2
Было бы чистым архитектором, если вы UIViewControllerTransitioningDelegate
какой-нибудь метод делегата UIViewControllerTransitioningDelegate
в свой ViewController, который хочет быть полумодальным.
Предполагая, что у нас есть ViewControllerA
присутствует ViewControllerB
с половиной модального 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
Джаннис хорошо поймал общую стратегию. Это не сработало для меня в iOS 9.x с быстрым 3. На представленном VC действие для запуска представленного VC похоже на то, что было представлено выше с некоторыми очень незначительными изменениями, как показано ниже:
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 в fooobar.com/questions/332781/.... Это была самая сложная часть для меня. Вот реализация протокола:
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.
Ответ 4
На всякий случай, если кто-то хочет сделать это со Swift 4, как я.
class MyViewController : UIViewController {
...
@IBAction func dictionaryButtonTouchUp(_ sender: UIButton) {
let modalViewController = ...
modalViewController.transitioningDelegate = self
modalViewController.modalPresentationStyle = .custom
self.present(modalViewController, animated: true, completion: nil)
}
}
extension MyViewController : UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController: presented, presenting: presenting)
}
}
Где класс HalfSizePresentationController состоит из:
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)
}
}
}
Ура!
Ответ 5
Чтобы добавить ответ Jannis:
В случае, если ваш pop-view является UIViewController, к которому вы добавили таблицу для загрузки/установки, вам необходимо убедиться, что созданный вами столбец соответствует желаемой ширине фактического представления.
Например:
let tableFrame: CGRect = CGRectMake(0, 0, chosenWidth, CGFloat(numOfRows) * rowHeight)
где selectedWidth - это ширина, которую вы установили в своем пользовательском классе (в приведенном выше примере: containerView.bounds.width)
Вам не нужно принудительно вводить в действие что-либо в самой ячейке, поскольку контейнер таблицы (по крайней мере теоретически) должен заставить ячейку иметь правую ширину.
Ответ 6
Я использую ниже логику, чтобы представить полуэкранный 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)