在iPad上访问UIActionSheet的UIPopoverController

16

在iPad上,可以使用-showFromBarButtonItem:animated:方法显示UIActionSheet。这很方便,因为它会将一个UIPopoverController包装到操作表周围,并将弹出窗口的箭头指向传入的UIBarButtonItem。

然而,这个调用会将UIBarButtonItem的工具栏添加到透传视图列表中,这并不总是理想的。而且,如果没有UIPopoverController的指针,就无法将其他视图添加到透传列表中。

有没有人知道一种官方认可的方法来获取指向弹出窗口控制器的指针?

提前感谢。

7个回答

10

您需要在方向更改时进行调整。

我找到了一种替代方案,对我来说完美地解决了问题。

坚持使用

- (void)showFromBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated

在你的@interface中添加

UIActionSheet *popoverActionsheet;

还可以添加

@property (nonatomic, retain) UIActionSheet *popoverActionsheet;

也可以添加

- (IBAction)barButtonItemAction:(id)sender;

所以您可以在实现的任何地方引用您的操作表。

在您的实现中

- (IBAction) barButtonItemAction:(id)sender
{
    //If the actionsheet is visible it is dismissed, if it not visible a new one is created.
    if ([popoverActionsheet isVisible]) {
        [popoverActionsheet dismissWithClickedButtonIndex:[popoverActionsheet cancelButtonIndex] 
                                                     animated:YES];
        return;
    }

    popoverActionsheet = [[UIActionSheet alloc] initWithTitle:nil
                                                     delegate:self
                                            cancelButtonTitle:nil 
                                       destructiveButtonTitle:nil
                         otherButtonTitles:@"Save Page", @"View in Safari", nil];

    [popoverActionsheet showFromBarButtonItem:sender animated:YES];
}

在你的actionsheet代理方法中实现

- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{

    if (buttonIndex == [actionSheet cancelButtonIndex]) return;

    //add rest of the code for other button indeces
}

不要忘记在适当的时候释放弹出操作表(popoverActionsheet)。

- (void)dealloc

我相信这样就可以了。


1
Stalinkay,谢谢。你所描述的与我现在正在做的非常接近。我应该更具体地说明。我从屏幕底部的工具栏中的按钮显示我的操作表。我还在屏幕顶部有一个导航栏。我希望导航栏中的按钮与工具栏中的按钮在行动表方面表现相同。换句话说,我希望导航栏按钮位于操作表的弹出窗口的穿透列表上,并且在导航栏按钮中点击同时解除操作表并处理按钮事件。这就是为什么获取指向弹出窗口的指针很重要的原因。 - westsider

9
    if ([[[[actionSheet superview] superview] nextResponder] respondsToSelector:@selector(setPassthroughViews:)]) {
        [[[[actionSheet superview] superview] nextResponder] performSelector:@selector(setPassthroughViews:) withObject:nil];
    }

这样做就可以了,这也不会引起任何与App Reviewers的问题,因为它没有调用任何私有API。

这种方法是脆弱的——if语句确保你的代码不会崩溃(只是不能工作),以防万一苹果公司改变了底层实现。


这是一个不错的解决方案。不过我想知道为什么苹果一开始会将该条添加到passthroughviews中。 - DrMickeyLauer
在iOS 6上,这对我不起作用。弹出控制器不在响应者链中。 - Greg Maletic
1
在我的iOS 6上运行得非常好!它必须在操作表呈现后调用。 - ToddH

4

我怀疑这方面没有官方的解决方案,因为操作表是在UIPopoverViews中呈现的,而UIPopoverViews与UIViewController相关联,而不是UIPopoverController。

NSLog(@"%@",[[[popoverActionsheet superview] superview] passthroughViews]);

浏览UIPopoverView头文件(未记录)可以得到以下解决方案,尽管未记录。如果您认为值得一试,请尝试,但不要想着瞒过应用程序审核员。 http://github.com/kennytm/iphone-private-frameworks/blob/master/UIKit/UIPopoverView.h
NSArray *passthroughViews = [[[popoverActionsheet superview] superview] passthroughViews];

        NSMutableArray *views = [passthroughViews mutableCopy];
        [views addObject:[self view]];
        [[[popoverActionsheet superview] superview] setPassthroughViews:views];
        [views release];

我认为这就是我在某种程度上正在寻找的东西。显然,它很脆弱,因为苹果可能会通过添加另一层视图等方式来改变其基础实现。我很想用弹出窗口编写自己的版本。当我开始时,我会在这里发布代码。再次感谢! - westsider
好的。期待看到你的成果。 - stalinkay

2

我已经使用nbransby的答案几年了,但在iOS 8中不再适用。然而,在iOS 8中,使用新的UIAlertController仍存在该问题。这是一个适用于我的更新:

UIAlertController *actionSheet = [UIAlertController
    alertControllerWithTitle:@"Test"
    message:@"Hello, world!"
    preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction
    actionWithTitle:@"OK"
    style:UIAlertActionStyleDefault
    handler:^(UIAlertAction *action) {}]];
actionSheet.modalPresentationStyle = UIModalPresentationPopover;
actionSheet.popoverPresentationController.barButtonItem = testButton;
[self presentViewController:actionSheet animated:TRUE completion:^(void) {
    actionSheet.popoverPresentationController.passthroughViews = [NSArray array];
}];

请注意,passthroughViews必须在弹出窗口出现后设置,但您可以方便地使用完成块来完成此操作。

2
我认为这是不可行的。我曾经也遇到过同样的问题。
使用

- (void)showFromRect:(CGRect)rect inView:(UIView *)view animated:(BOOL)animated

例子:

[actionSheet showFromRect:[[[myToolBar subviews] objectAtIndex:0] frame] inView:myToolBar animated:YES];

注意:您必须更改 objectAtIndex: 的索引以适应您的情况,并参考您的工具栏。

这个能在方向改变时工作吗?还是需要在旋转事件上进行调整?谢谢。 - westsider

2

这是解决方案。我刚刚发现它,非常简单。在显示操作表之前将userInteractionEnabled设置为NO,在操作表委托方法结束时将其设置为YES以进行解除。

- (void)promptResetDefaults:(id)sender
{
    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
                                                             delegate:self
                                                    cancelButtonTitle:nil
                                               destructiveButtonTitle:NSLocalizedString(@"Reset All Defaults", nil)
                                                    otherButtonTitles:nil];

    self.navigationController.navigationBar.userInteractionEnabled = NO;
    [actionSheet showFromBarButtonItem:sender animated:YES];
}

- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == actionSheet.destructiveButtonIndex)
    {
        [self.dataDefaults resetDataDefaults];
        [self.tableView reloadData];
    }

    self.navigationController.navigationBar.userInteractionEnabled = YES;
}

非常好的解决方法(甚至可能是最好的)。我不知道为什么人们不给它点赞。 - Byte

1

当从一个条形按钮项进行演示时,通常希望在操作表被解除之前禁用工具栏上的所有其他按钮。

我认为最简单的方法是修改UIToolbar类。您需要一种方法来获取操作表,例如通过在应用程序委托中保存它。

创建一个名为UIToolbar+Modal.m的文件,如下所示:

@implementation UIToolbar (Modal)

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  UIView *hitView = [super hitTest:point withEvent:event];

  UIActionSheet *mySheet = [[[UIApplication sharedApplication] delegate] myActionSheet];
  if ([mySheet isVisible]) return nil;
  else return hitView;
}

@end

头文件不需要包含任何特殊内容

@interface UIToolbar (Modal) {
}
@end

顺便提一下,在找到这个解决方案之前,我尝试将一个空数组分配给passthroughViews,你可以像stalinkay最初描述的那样访问它,但实际上这并不起作用(除了未记录在文档中)。
其他方法都有缺点-要么必须处理方向更改,要么工具栏按钮看起来像按下,而实际上唯一的操作是关闭操作表。
更新
自iOS 4.3以来,这种方法不再起作用。 您需要子类化:

UIToolbarWithModal.h

#import <Foundation/Foundation.h>

@interface UIToolbarWithModal : UIToolbar {
}

@end

记住,当你创建操作表时,需要保持其可访问性,可以在你的应用程序委托中或者在 myActionSheet 属性中存储。

UIToolbarWithModal.m

#import "UIToolbarWithModal.h"
#import "MyDelegate.h"

@implementation UIToolbarWithModal

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  UIView *hitView = [super hitTest:point withEvent:event];

  UIActionSheet *mySheet = [[[UIApplication sharedApplication] delegate] myActionSheet];
  if ([mySheet isVisible]) return nil;
  else return hitView;
}

@end

然后在您以前使用UIToolbar的代码中,只需使用UIToolbarWithModal即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接