Могу ли я сделать UIPresentationController иметь userInteractionEnabled на PresentingViewController?

Я создаю пользовательский графический интерфейс для универсального приложения для iPhone и iPad. На iPad он в значительной степени опирается на "sideViews" для таких утилит, как манипулирование контентом, detailInformation и т. П. (Вспомним расширенный SplitView). С визуальной точки зрения новый UIPresentationController позволяет мне представить эти "боковые виды" (без использования dimmedView), и его реализация проста в создании и обслуживании, и в то же время прекрасно интегрируется с раскадровкой. Но мне нужно иметь возможность манипулировать содержимым presentingViewController, когда представленный ViewController является видимым. Поэтому мой вопрос заключается в том, могу ли я установить userInteractionEnabled (или аналогичный) для presentingViewController при представлении боковых представлений?

4 ответа

Решение

UIPresentationController вставляет свой контейнерный вид в качестве подпредставления окна над представлением представления, таким образом, любые касания за пределами представленного представления захватываются представлением контейнера и никогда не попадают в представление представления.

Исправление заключается в том, чтобы вставить представление в контейнерное представление, которое проходит через касания в представление представления. Вы можете использовать это как вид затемнения или установить его backgroundColor в [UIColor clearColor] для полностью прозрачного просмотра. Установите сквозные представления в коде контроллера презентаций.

@interface IVPasserView : UIView

@property (strong, nonatomic) NSArray* passthroughViews;

@end

@implementation IVPasserView

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView* hit = [super hitTest:point withEvent:event];
    if (hit == self)
        for (UIView* passthroughView in _passthroughViews)
        {
            hit = [passthroughView hitTest:[self convertPoint:point toView:passthroughView]
                                 withEvent:event];
            if (hit)
                break;
        }
    return hit;
}

@end

Примечание: пока это нарушает дух -[UIView hitTest:withEvent:] в том, что он не возвращает подпредставление, это на самом деле, как стандарт системы UIPopoverPresentationController обрабатывает это. Если вы установите passthroughViews свойство там, представление контейнера отвечает hitTest:withEvent: с сквозным видом, даже если они не супервизор / подпредставление! Таким образом, он может выжить в следующем выпуске iOS.

Модальная презентация не подходит для вашей ситуации. Лучше реализовать ваш сценарий с помощью настраиваемого контроллера представления контейнера и переопределения showDetailViewController:sender: метод для обработки представления дополнительных контроллеров представления. Вы можете адаптировать этот метод для отображения модального контроллера представления на iPhone и справа на iPad, например.

Вот выдержка из документации Apple:

Представление против показа контроллера представления

Класс UIViewController предлагает два способа отображения контроллера представления:

Методы showViewController:sender: и showDetailViewController:sender: предлагают наиболее адаптивный и гибкий способ отображения контроллеров представления. Эти методы позволяют представителю представления решить, как лучше всего обрабатывать презентацию. Например, контроллер представления контейнера может включать контроллер представления как дочерний элемент вместо представления его модально. Поведение по умолчанию представляет контроллер представления модально. PresentViewController: animated: завершение: метод всегда отображает контроллер представления модально. Контроллер представления, который вызывает этот метод, может в конечном итоге не обрабатывать презентацию, но презентация всегда модальная. Этот метод адаптирует стиль представления для горизонтально компактных сред. Методы showViewController:sender: и showDetailViewController:sender: являются предпочтительным способом инициирования презентаций. Контроллер представления может вызывать их, ничего не зная об остальной части иерархии контроллера представления или текущем положении контроллера представления в этой иерархии. Эти методы также упрощают многократное использование контроллеров представления в различных частях вашего приложения без написания условных путей кода.

Итак, похоже, что идея UIPresentationController НЕ в том, чтобы использовать его как расширенный SplitView (или, по крайней мере, это мой текущий вывод). Мне все-таки удалось найти обходной путь. Если кто-нибудь найдет лучший способ справиться с этим, пожалуйста, дайте мне знать в комментариях.

Поэтому я вставляю представление PresentingViewController в контейнерное представление transitionContexts (так же, как контейнерное представление UIPresentationControllers) в индекс 0. Это позволяет мне прозрачно обрабатывать touchEvents в представлении PresentingViewControllers. Но он удаляет представление PresentingViewControllers из исходной иерархии представлений, поэтому мне нужно переместить его туда, когда презентация отклоняется. Это означает возвращение представления обратно к представлению parentViewController, если оно присутствует, или к окну приложения, если представляет ViewController, являющийся rootViewController приложения (могут быть и другие сценарии, но пока это будет сделано).

Все это делается в animateTransition в UIViewControllerAnimatedTransitioning.

Вот кусок кода:

UIView.animateWithDuration(transitionDuration(transitionContext),
        delay: 0.0,
        usingSpringWithDamping: 1.0,
        initialSpringVelocity: 0.5,
        options: UIViewAnimationOptions.BeginFromCurrentState|UIViewAnimationOptions.AllowUserInteraction,
        animations: { () -> Void in
            animatingView.frame = finalFrame
        }) { (finished:Bool) -> Void in
            if !self.isPresentation {
                if let parentViewController = backgroundVC.parentViewController {
                    parentViewController.view.addSubview(backgroundVC.view)
                }
                else if let window = (UIApplication.sharedApplication().delegate as! AppDelegate).window {
                    window.addSubview(backgroundVC.view)
                }
                fromView.removeFromSuperview()
            }
            else {
                containerView.insertSubview(backgroundVC.view, atIndex: 0)
            }
            transitionContext.completeTransition(true)
    }

Адаптация отличного решения Objective C от Глена Лоу к Swift.

      // View that supports forwarding hit test touches to the provided array of views
class TouchForwardView: UIView {
    
    // Array of views that we want to forward touches to
    var touchForwardTargetViews = [UIView]()
    
    // Variable to quickly disable/enable touch forwarding functionality
    var touchForwardingEnabled = true
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // Get the hit test view
        let hitTestView = super.hitTest(point, with: event)
        
        // Make sure the hit test view is self and that touch forwarding is enabled
        if hitTestView == self && touchForwardingEnabled {
            // Iterate the hit test target views
            for targetView in touchForwardTargetViews {
                // Convert hit test point to the target view
                let convertedPoint = convert(point, to: targetView)
                // Verify that the target view can receive the touch
                if let hitTargetView = targetView.hitTest(convertedPoint, with: event) {
                    // Forward the touch to the target view
                    return hitTargetView
                }
            }
        }
        
        // Return the original hit test view - this is the super value - our implmentation didn't affect the hit test pass
        return hitTestView
    }
    
}
Другие вопросы по тегам