iOS 8 presentationController确定是否真的是popover

时间:2014-11-01 07:12:06

标签: ios8 uipopovercontroller adaptive-layout

我正在使用iOS 8的新自适应“Present As Popover”功能。我在StoryBoard中连接了一个简单的segue来进行演示。它适用于iPhone 6 Plus,因为它将视图显示为弹出窗口,而iPhone 4s则显示为全屏视图(工作表样式)。

问题是当显示为全屏视图时,我需要在视图中添加“完成”按钮,以便可以调用dismissViewControllerAnimated。当它显示为弹出窗口时,我不想显示“完成”按钮。

enter image description here

我试着查看了presentationController和popoverPresentationController的属性,但是我找不到任何告诉我它是否实际显示为弹出窗口的内容。

NSLog( @"View loaded %lx", (long)self.presentationController.adaptivePresentationStyle );          // UIModalPresentationFullScreen
NSLog( @"View loaded %lx", (long)self.presentationController.presentationStyle );                  // UIModalPresentationPopover
NSLog( @"View loaded %lx", (long)self.popoverPresentationController.adaptivePresentationStyle );   // UIModalPresentationFullScreen
NSLog( @"View loaded %lx", (long)self.popoverPresentationController.presentationStyle );           // UIModalPresentationPopover

adaptivePresentationStyle始终返回UIModalPresentationFullScreen,而presentationStyle始终返回UIModalPresentationPopover

在查看UITraitCollection时,我确实找到了一个名为“_UITraitNameInteractionModel”的特征,当它实际显示为Popover时,它只被设置为1。但是,Apple不通过popoverPresentationController的traitCollection直接访问该特征。

8 个答案:

答案 0 :(得分:13)

我发现这样做的最好方法(最不臭)是使用UIPopoverPresentationControllerDelegate

•确保将呈现的视图控制器设置为用于管理演示文稿的UIPopoverPresentationControllerDelegate上的UIPopoverPresentationController。我使用了故事板,因此请在prepareForSegue:

中进行设置
segue.destinationViewController.popoverPresentationController.delegate = presentedVC;

•在呈现的视图控制器中创建一个属性以跟踪此状态:

@property (nonatomic, assign) BOOL amDisplayedInAPopover;

•添加以下委托方法(或添加到现有的委托方法):

- (void)prepareForPopoverPresentation:(UIPopoverPresentationController *)popoverPresentationController
{
    // This method is only called if we are presented in a popover
    self.amDisplayedInAPopover = YES;
}

•最后在viewWillAppear: - viewDidLoad:中过早,在viewDidLoad:viewWillAppear:之间调用委托准备方法

if (self.amDisplayedInAPopover) {
    // Hide the offending buttons in whatever manner you do so
    self.navigationItem.leftBarButtonItem = nil;
}

编辑:更简单的方法!

只需设置代理(确保您的presentVC采用UIPopoverPresentationControllerDelegate):

segue.destinationViewController.popoverPresentationController.delegate = presentedVC;

并提供方法:

- (void)prepareForPopoverPresentation:(UIPopoverPresentationController *)popoverPresentationController
{
    // This method is only called if we are presented in a popover
    // Hide the offending buttons in whatever manner you do so
    self.navigationItem.leftBarButtonItem = nil;
}

答案 1 :(得分:10)

我检查了视图布局后是否设置了popoverPresentationController的arrowDirection。就我的目的而言,这种方法效果很好,涵盖了较小屏幕设备上的弹出窗口。

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    if (popoverPresentationController?.arrowDirection != UIPopoverArrowDirection.Unknown) {
        // This view controller is running in a popover
        NSLog("I'm running in a Popover")
    }
}

答案 2 :(得分:3)

我测试了这篇文章中提出的所有解决方案。对不起,在所有情况下都无效。例如,在iPad中,拆分视图显示样式可以在拖动拆分视图行时更改,因此我们需要特定的通知。 经过几个小时的研究,我发现苹果样品中的溶液(swift): https://developer.apple.com/library/ios/samplecode/AdaptivePhotos/Introduction/Intro.html#//apple_ref/doc/uid/TP40014636

这是obj-c中的相同解决方案。

首先在prepareForSegue函数中设置popoverPresentationController委托。它也可以在MyViewController“init”中设置,但不能在“viewDidLoad”中设置(因为在viewDidLoad之前调用第一个willPresentWithAdaptiveStyle)。

MyViewController *controller = [segue destinationViewController];
        controller.popoverPresentationController.delegate = (MyViewController *)controller;

现在,每次iOS更改演示风格时,MyViewController对象都会收到此通知,包括首次演示。以下是在navigationController中显示/隐藏“关闭”按钮的示例实现:

- (void)presentationController:(UIPresentationController *)presentationController
  willPresentWithAdaptiveStyle:(UIModalPresentationStyle)style
         transitionCoordinator:(nullable id<UIViewControllerTransitionCoordinator>)transitionCoordinator {
    if (style == UIModalPresentationNone) {
        // style set in storyboard not changed (popover), hide close button
        self.topViewController.navigationItem.leftBarButtonItem = nil;
    } else {
        // style changed by iOS (to fullscreen or page sheet), show close button
        UIBarButtonItem *closeButton =
            [[UIBarButtonItem alloc] initWithTitle:@"Close" style:UIBarButtonItemStylePlain target:self action:@selector(closeAction)];
        self.topViewController.navigationItem.leftBarButtonItem = closeButton;
    }
}

- (void)closeAction {
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}

答案 3 :(得分:2)

管理视图控制器的UIPresentationController通过将modalPresentationStyle设置为UIModalPresentationPopover来呈现它。

根据UIViewController reference

  

presentsViewController

     
      
  • 显示此视图的视图控制器   控制器。 (只读)
  •   
     

modalPresentationStyle

     
      
  • UIModalPresentationPopover:在水平常规环境中,在弹出视图中显示内容的演示文稿样式。背景内容变暗并点击   在popover之外导致popover被解雇。如果你不   想要点击以消除弹出窗口,您可以分配一个或多个视图   相关的passthroughViews属性   UIPopoverPresentationController对象,你可以从中获取   popoverPresentationController属性。
  •   

我们可以通过检查horizontalSizeClass如下(我假设您的按钮是UIBarButtonItem

来确定您的视图控制器是在弹出框内还是以模态方式呈现
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    if (self.presentingViewController.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular)
        self.navigationItem.leftBarButtonItem = nil; // remove the button
}

最安全的地方是viewWillAppear:presentingViewController可能是nil

答案 4 :(得分:2)

怎么样

if (self.modalPresentationStyle == UIModalPresentationPopover)

这对我有用

答案 5 :(得分:2)

实现此功能的官方方法是首先从视图控制器中删除“完成”按钮,然后在适应紧凑时将视图控制器嵌入导航控制器中,将完成按钮添加为导航项:

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
    return UIModalPresentationStyle.FullScreen
}

func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
    let navigationController = UINavigationController(rootViewController: controller.presentedViewController)
    let btnDone = UIBarButtonItem(title: "Done", style: .Done, target: self, action: "dismiss")
    navigationController.topViewController.navigationItem.rightBarButtonItem = btnDone
    return navigationController
}

func dismiss() {
    self.dismissViewControllerAnimated(true, completion: nil)
}

Full Tutorial

Screenshots

答案 6 :(得分:2)

适用于多任务的解决方案

将呈现控制器指定为popover的代理

...
controller.popoverPresentationController.delegate = controller;
[self presentViewController:controller animated:YES completion:nil];

然后,在控制器中,实现委托方法:

- (void)presentationController:(UIPresentationController *)presentationController willPresentWithAdaptiveStyle:(UIModalPresentationStyle)style transitionCoordinator:(id<UIViewControllerTransitionCoordinator>)transitionCoordinator
{
    if (style != UIModalPresentationNone)
    {
        // Exited popover mode
        self.navigationItem.leftBarButtonItem = button;
    }
}

- (void)prepareForPopoverPresentation:(UIPopoverPresentationController *)popoverPresentationController
{
    // Entered popover mode
    self.navigationItem.leftBarButtonItem = nil;
}

答案 7 :(得分:1)

我的棘手解决方案,效果很好。

import React, { Component } from 'react'; import { StyleSheet, View, Text, PanResponder, Animated, Easing, Dimensions, Platform, TouchableOpacity, } from 'react-native'; let CIRCLE_RADIUS = 36; let Window = Dimensions.get('window'); export class App extends Component<{}> { constructor(props){ super(props); this.dataDrag = [1,2,3,4]; this.pan = this.dataDrag.map( () => new Animated.ValueXY() ); this.state = { showDraggable : true, dropZoneValues : null, }; } getPanResponder(index) { return PanResponder.create({ onStartShouldSetPanResponder: () => true, onPanResponderMove : Animated.event([null,{ dx : this.pan[index].x, dy : this.pan[index].y }]), onPanResponderRelease : (e, gesture) => { if(this.isDropZone(gesture)){ this.setState({ showDraggable : false }); }else{ Animated.spring( this.pan[index], {toValue:{x:0,y:0}} ).start(); } } }); } isDropZone(gesture){ var dz = this.state.dropZoneValues; return gesture.moveY > dz.y && gesture.moveY < dz.y + dz.height; } setDropZoneValues(event){ this.setState({ dropZoneValues : event.nativeEvent.layout }); } render(){ return ( <View style={styles.mainContainer}> <View onLayout={this.setDropZoneValues.bind(this)} style={styles.dropZone}> <Text style={styles.text}>Drop me here!</Text> </View> {this.dataDrag.map((d, index) => ( <Animated.View key={index} {...this.getPanResponder(index).panHandlers} style={[styles.draggableContainer, this.pan[index].getLayout(), styles.circle]}> <Text style={styles.text}>Drag {index}</Text> </Animated.View> ))} </View> ); } } let styles = StyleSheet.create({ mainContainer: { flex : 1 }, dropZone : { height : 100, backgroundColor:'#2c3e50' }, text : { marginTop : 25, marginLeft : 5, marginRight : 5, textAlign : 'center', color : '#fff' }, draggableContainer: { position : 'absolute', marginTop : Window.height/2 - CIRCLE_RADIUS, marginLeft : Window.width/2 - CIRCLE_RADIUS, }, circle : { backgroundColor : '#1abc9c', width : CIRCLE_RADIUS*2, height : CIRCLE_RADIUS*2, borderRadius : CIRCLE_RADIUS }, }); 的{​​{1}}。

PopoverViewController

这个想法很简单,Popover的视图大小永远不会等于设备屏幕大小,除非它不是Popover。