我正在为 iPhone 和 iPad 的通用应用程序构建自定义 GUI。在 iPad 上,它严重依赖“sideViews”来实现内容操作、detailInformation 等实用程序(想想高级的 SplitView)。从视觉的角度来看,新的 UIPresentationController 可以让我展示这些“sideViews”(而不是使用 dimmedView),并且实现很容易构建和维护,同时仍然很好地与故事板集成。但是我需要能够在presentingViewController 可见时操作presentingViewController 的内容。所以我的问题是,我可以在呈现侧视图时在presentingViewController 上设置userInteractionEnabled(或类似的)吗?
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:completion: 方法总是以模态方式显示视图控制器。调用此方法的视图控制器可能最终不会处理演示文稿,但演示文稿始终是模态的。此方法使呈现风格适应水平紧凑的环境。showViewController:sender: 和 showDetailViewController:sender: 方法是启动演示的首选方式。视图控制器可以在不知道视图控制器层次结构的其余部分或当前视图控制器在该层次结构中的位置的情况下调用它们。这些方法还可以更轻松地在应用程序的不同部分重用视图控制器,而无需编写条件代码路径。
好的,所以 UIPresentationController 的想法似乎不能将其用作高级 SplitView(或者至少这是我目前的结论)。不过,我确实设法建立了一个解决方法。如果有人找到更好的处理方法,请在评论中告诉我。
所以我要做的是在索引 0 处的 transitionContexts containerView(与 UIPresentationControllers containerView 相同)层次结构中插入 PresentingViewController 的视图。这使我能够透明地处理 PresentingViewControllers 视图中的 touchEvents。但是它从其原始视图层次结构中删除了 PresentingViewControllers 视图,因此当演示文稿被解除时,我需要将其移回那里。这意味着将视图放回 parentViewController 的视图(如果存在)或应用程序的窗口,如果 presentingViewController 是应用程序的 rootViewController(可能还有其他场景,但现在可以这样做)。
这都是在 UIViewControllerAnimatedTransitioning 中的 animateTransition 中完成的。
这是一段代码:
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)
}
将 Glen Low 的上述出色的 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
}
}