【问题标题】:UIStoryboardPopoverSegue opening multiple windows on button touchUIStoryboardPopoverSegue 在按钮触摸时打开多个窗口
【发布时间】:2011-12-07 05:07:57
【问题描述】:

我正在使用UIStoryboardPopoverSegue 为 iOS 5 iPad 应用程序呈现弹出框。 Segue 效果很好,但似乎包含按钮的工具栏是弹出框控制器的直通视图,因此如果您继续按下按钮,则会出现更多弹出框。由于我自己没有创建和跟踪UIPopoverController(正如故事板所做的那样),当再次触摸按钮时我无法关闭它。有没有其他人遇到过这个?我向 Apple 提出了一个错误,但他们没有回应。

编辑:我已经使用下面的答案解决了这个问题。这是我最终使用的代码。 currentPopover 是我的视图控制器类中的 __weak ivar,所以当控制器完成后,它会自动降为零。

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue isKindOfClass:[UIStoryboardPopoverSegue class]]){
        // Dismiss current popover, set new popover
        [currentPopover dismissPopoverAnimated:YES];
        currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
    }
}

【问题讨论】:

  • 感谢您的编辑,一个优雅的问题解决方案!

标签: iphone ios5


【解决方案1】:

2013 年 6 月 14 日

感谢您的编辑。而不是关闭和重新创建视图控制器——以避免性能和电池问题并在关闭和重新创建视图控制器时防止 Flash——如何防止弹出的第二个实例弹出?

//place in view controller (tested iOS6+, iPad, iPhone)
__weak UIPopoverController *popover;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if([segue isKindOfClass:[UIStoryboardPopoverSegue class]] 
        && [segue.identifier isEqualToString:@"mySegue"]) //remember to change "mySegue" 
            popover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
    if ([identifier isEqualToString:@"mySegue"]) //remember to change "mySegue"
        return !popover;
    else
        return YES;
}
added checks to: http://stackoverflow.com/a/10238581/1705353 

【讨论】:

  • 我喜欢。可以将我原始帖子编辑中的代码与您的 shouldPerform 代码结合起来,这将使它比我的编辑代码本身在视觉上更令人愉悦。
【解决方案2】:

我更喜欢使用静态弱变量,它将所有内容放在一个地方:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender  
{
    if ([[segue identifier] isEqualToString:@"showSomething"]) {
        static __weak UIPopoverController* currentPopover = nil;
        [currentPopover dismissPopoverAnimated:NO];
        currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
        // ...
    }
}

没有理由添加一个单独的额外变量(你什么时候会有多个视图控制器实例?),这样你就可以为每个 if() 块添加一个额外的变量。

【讨论】:

    【解决方案3】:

    只需通过IBAction 连接UIBarButtonItem。使用 interface builder 中设置的 itendifier:

    -(IBAction)barButtonItemPressed:(id)sender {
        if (currentPopoverController && currentPopoverController.popoverVisible) {
            [currentPopoverController dismissPopoverAnimated:YES];
            currentPopoverController = nil;
        } else {
            [self performSegueWithIdentifier:@"aSegueIdentifier" sender:sender];
        }
    }
    

    从序列中获取新UIPopoverCOntroller的引用:

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
        if ([[segue identifier] isEqualToString:@"aSegueIdentifier"])
            currentPopoverController = [(UIStoryboardPopoverSegue *)segue popoverController];
    }
    

    currentPopoverController是一个实例变量,定义在头文件中:

    UIPopoverController *currentPopoverController;
    

    重要提示:sque的anchor属性必须设置为对应的UIBarButtonItem!

    【讨论】:

    • 除非你还在didDismiss 上将currentPopoverController 的代表设置为零,否则你仍然会在这里遇到多个弹出窗口问题。 My preferred workaround 与此基本相同,但我将currentPopoverController 设为弱引用——这样无论弹出框如何关闭,它都会自动获取nil
    【解决方案4】:

    此解决方案也可能存在视觉问题,但不适用于我的简单案例。就我而言,弹出框只是显示了一些帮助。我将以下内容(使用 ARC)放在一起,当第二次按下按钮栏按钮(原始的和新创建的)时,将关闭弹出视图控制器。

    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {   
        if( [segue isKindOfClass:[UIStoryboardPopoverSegue class]] )
        {
            UIStoryboardPopoverSegue *popoverSegue      = (id)segue;
            UIPopoverController      *popoverController = popoverSegue.popoverController;
    
            if( m_popoverController.popoverVisible )
            {
                [m_popoverController dismissPopoverAnimated:NO];
                dispatch_async( dispatch_get_main_queue(), ^{
                    [popoverController dismissPopoverAnimated:YES];
                });
                m_popoverController = nil;
            }
            else
                m_popoverController = popoverController;        
        }    
    }
    

    我还在 dealloc 中添加了一些清理

    - (void)dealloc
    {
        if( m_popoverController.popoverVisible )
            [m_popoverController dismissPopoverAnimated:YES];
    }
    

    它确实需要你的类中的成员变量

    UIPopoverController *m_popoverController;
    

    【讨论】:

      【解决方案5】:

      您的解决方案 Cory 存在一些视觉问题。

      可以考虑的两个选项 - 只需删除或更改显示弹出框的按钮的操作。

      选项1,保持一个指向按钮动作的指针,在弹出框出现后,将动作设置为nil。解除弹出框后重置为原始操作。

      -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
      {
      
          action = [sender action];
          [sender setAction:nil];
      
          self.currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
          self.currentPopover.delegate = self;
      }
      
      -(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
      {
          [self.navigationItem.rightBarButtonItem setAction:action];
      
          return YES;
      }
      

      这样弹出框只能出现一次,并且会按预期消失。

      第二个选项是更改按钮的功能,以便当弹出框可见时,点击按钮将导致弹出框被关闭。

          -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
          {
      
              action = [sender action];
              target = [sender target];
      
              [sender setTarget:self];
              [sender setAction:@selector(dismiss:)];
      
              self.currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
              self.currentPopover.delegate = self;
          }
      
      -(void)dismiss:(id)sender
      {
          [self.navigationItem.rightBarButtonItem setAction:action];
          [self.navigationItem.rightBarButtonItem setTarget:target];
          ////or
      //  [sender setAction:action];
      //  [sender setTarget:target];
          [self.currentPopover dismissPopoverAnimated:YES];
      }
      
      
          -(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
          {
              [self.navigationItem.rightBarButtonItem setAction:action];
              [self.navigationItem.rightBarButtonItem setTarget:target];
      
              return YES;
          }
      

      【讨论】:

      • Robert 的解决方案非常适合我。我认为这就是苹果在 UISplitViewController 中实现这种技术的方式。只是一个插件,你需要添加两个ivars:SEL动作;识别目标;干杯!
      • 太棒了!我唯一要添加的是另一个 ivar:id myPopButton,并在 prepareForSegue 中设置:myPopButton = sender;这样,您可以引用多个按钮(当从工具栏调用时,'fristance)。在 'dismiss' 和 'popoverControllerShouldDismissPopover' 方法中,调用: [myPopButton setAction: action];和 [myPopButton setTarget: 目标];对此进行了测试,效果很好(iOS SDK 6.0)。
      【解决方案6】:

      这也不错。

      @interface ViewController : UIViewController <UIPopoverControllerDelegate> {
          UIPopoverController * seguePopoverController;
      }
      
      @property (strong) UIPopoverController * seguePopoverController;
      
      @end
      
      - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender  
      {
          if (self.seguePopoverController) {
              [self.seguePopoverController dismissPopoverAnimated:NO];
              self.seguePopoverController = nil;
          }
      
          // The Storyboard Segue is named popover in this case:
          if ([[segue identifier] isEqualToString:@"popover"]) {
      
              UIStoryboardPopoverSegue* popSegue = (UIStoryboardPopoverSegue*)segue;
              UIPopoverController *thePopoverController = [popSegue popoverController];
              thePopoverController.delegate = self;
              self.seguePopoverController = thePopoverController;
      
          }
      }
      
      - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
      {
          self.seguePopoverController = nil;
      }
      

      【讨论】:

      • 如果你使用我添加到问题中的代码,weak ivar 将自动降为零,这将消除对一堆代码的需求。如果您添加另一个 popover segue 并忘记将其命名为“popover”,则顶部的代码也将起作用。
      【解决方案7】:

      您必须在prepareForSegue 类方法中存储对作为UIStoryboardPopoverSegue 类的一部分传递的popoverController 属性的引用。

      要访问它,请像这样覆盖调用视图控制器中的方法:

      - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
      {
          // The Storyboard Segue is named popover in this case:
          if ([segue.identifier compare:@"popover"] == NSOrderedSame) {
              // segue.popoverController is only present in popover segue's
              // self.seguePopoverController is a UIPopoverController * property.
              self.seguePopoverController = segue.popoverController;
          }
      }
      

      然后你可以用通常的方式关闭它。

      【讨论】:

      • 虽然我希望 Apple 提供一些更自动化的东西,但这确实有效。我已经为感兴趣的人添加了我在原始帖子中使用的代码。
      • 太棒了!这种方法在为 iOS 7 编译的代码上崩溃,该代码在装有 iOS 8.2 的 iPad 上运行。您必须使用 unwind segues 来消除弹出窗口。谢谢苹果。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-03
      • 1970-01-01
      • 1970-01-01
      • 2020-12-13
      • 1970-01-01
      • 2012-06-17
      • 1970-01-01
      相关资源
      最近更新 更多