当从UIBarButtonItem呈现方向更改后,如何防止UIPopoverController passthroughViews被重置?

时间:2014-04-23 02:21:50

标签: ios uipopovercontroller uibarbuttonitem

我有一个UIPopoverController,它是从UIBarButtonItem呈现的。我想在popover之外触摸以解除popover。当从条形按钮呈现弹出框时,其他条形按钮将自动包含在弹出框直通视图中。为了防止我在呈现弹出窗口后将passthrough视图设置为nil(或@ []),如下所示:

- (IBAction) consoleBarButtonHit:(id)sender {
UIViewController *consoleNavigationController=[self.storyboard instantiateViewControllerWithIdentifier:@"consoleNavigationController"];
_consolePopoverController=[[UIPopoverController alloc] initWithContentViewController:consoleNavigationController];
_consolePopoverController.delegate=self;

[_consolePopoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

// this must be done _after_ presenting the popover in order to work
_consolePopoverController.passthroughViews=nil;
}

这一切都很好,但是我遇到的问题是,在弹出设备后,当弹出窗口可见时,条形按钮会自动重新添加为直通视图而不会导致弹出窗口被解雇

如果我能以某种方式获得条形按钮视图(或矩形),那么我可以使用

呈现弹出窗口
-presentPopoverFromRect:inView:permittedArrowDirections:animated:

这可能会解决这个问题,但我不知道从UIBarButtonItem找到rect / view的任何非hackish方式。

我真的不希望当其他按钮被按下时调用的选择器以编程方式关闭弹出框,这不是他们的责任,可能会在以后给我带来问题。

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

所以我提出了一个解决方案,这有点奇怪,但保持模块化,运行良好。我创建了一个名为PropertyEnforcer的类,它将自己注册为对象属性的KVO观察者,并在它发生更改时重新设置该属性。

PropertyEnforcer.h:

#import <Foundation/Foundation.h>

@interface PropertyEnforcer : NSObject

+ (void) enforceProperty:(NSString*)keyPath ofObject:(id)target toValue:(id)value;

@end

PropertyEnforcer.m:

#import "PropertyEnforcer.h"
#import <objc/runtime.h>

@interface PropertyEnforcer ()

@property (retain) NSString *keyPath;
@property (retain) id value;
@property (assign) id target;

@end

@implementation PropertyEnforcer

- (void) dealloc {
    [_target removeObserver:self forKeyPath:_keyPath context:NULL];
    [super dealloc];
}

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if( (([_target valueForKey:_keyPath] == nil) && (_value==nil)) || [[_target valueForKey:_keyPath] isEqual:_value]) {
        return;
    } else {
        [_target setValue:_value forKeyPath:_keyPath];
    }
}

+ (void) enforceProperty:(NSString*)keyPath ofObject:(id)target toValue:(id)value {
    PropertyEnforcer *enforcer=[[PropertyEnforcer alloc] init];
    enforcer.value=value;
    enforcer.keyPath=keyPath;
    enforcer.target=target;

    [target addObserver:enforcer forKeyPath:keyPath options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:NULL];

    objc_setAssociatedObject(target,
                             _cmd, // using this technique we can only attach one PropertyEnforcer per target
                             enforcer,
                             OBJC_ASSOCIATION_RETAIN);

    [enforcer release];
}

@end

现在我可以将原始代码的更改更改为:

- (IBAction) consoleBarButtonHit:(id)sender {
    UIViewController *consoleNavigationController=[self.storyboard instantiateViewControllerWithIdentifier:@"consoleNavigationController"];
    _consolePopoverController=[[UIPopoverController alloc] initWithContentViewController:consoleNavigationController];
    _consolePopoverController.delegate=self;

    [_consolePopoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

    // make sure those passthroughViews are always nil !
    [PropertyEnforcer enforceProperty:@"passthroughViews" ofObject:_consolePopoverController toValue:nil];
}

PropertyEnforcer将自身注册为关联对象,因此我们无需跟踪它。它会自动注销自己作为KVO观察者,并在UIPopoverController被销毁时被销毁。

这是我能提出的最好,最不具说服力的解决方案。

答案 1 :(得分:0)

我选择的解决方案是单独离开passthroughViews,而在显示UIBarButtonItem时,禁用/重新启用工具栏或导航栏中的各个按钮(UIPopoverPresentationController个实例)基于其过渡而被解雇。

(iOS 8:UIPopoverPresentationController代替UIPopoverController。)

<强> UIPopoverPresentationController + managedBarButtonItems.h

@interface UIPopoverPresentationController (managedBarButtonItems)

@property (nonatomic, retain) NSArray* managedBarButtonItems;

@end

<强> UIPopoverPresentationController + managedBarButtonItems.m

#import "UIPopoverPresentationController+managedBarButtonItems.h"

#import <objc/runtime.h>

//
// scope: private, in-terms-of
//

@interface UIBarButtonItem (wasEnabled)

@property (nonatomic) BOOL wasEnabled;

@end

@implementation UIBarButtonItem (wasEnabled)

- (BOOL)wasEnabled
{
    return [objc_getAssociatedObject(self, @selector(wasEnabled)) boolValue];
}

- (void)setWasEnabled:(BOOL)wasIt
{
    objc_setAssociatedObject(self, @selector(wasEnabled), [NSNumber numberWithBool:wasIt], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

// FYI: "Associated objects are released [automatically] after the dealloc method of the original object has finished."

@end

//
// scope: consumable
//

@implementation UIPopoverPresentationController (managedBarButtonItems)

- (NSArray*)managedBarButtonItems
{
    return objc_getAssociatedObject(self, @selector(managedBarButtonItems));
}

- (void)setManagedBarButtonItems:(NSArray*)items
{
    objc_setAssociatedObject(self, @selector(managedBarButtonItems), items, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

// FYI: "Associated objects are released [automatically] after the dealloc method of the original object has finished."

- (void)presentationTransitionDidEnd:(BOOL)completed
{
    [super presentationTransitionDidEnd:completed];

    if (self.barButtonItem && self.managedBarButtonItems)
    {
        for (UIBarButtonItem* button in self.managedBarButtonItems)
        {
            if (button.action != /* actuator */ self.barButtonItem.action)
            {
                button.wasEnabled = button.enabled, button.enabled = NO;
            }
        }
    }
}

- (void)dismissalTransitionDidEnd:(BOOL)completed
{
    [super dismissalTransitionDidEnd:completed];

    if (self.barButtonItem && self.managedBarButtonItems)
    {
        for (UIBarButtonItem* button in self.managedBarButtonItems)
        {
            if (button.action != /* actuator */ self.barButtonItem.action)
            {
                button.enabled = button.wasEnabled;
            }
        }
    }
}

@end

<强>用法:

UIAlertController* actionSheet = [UIAlertController
    alertControllerWithTitle:@"Actions" message:nil
        preferredStyle:UIAlertControllerStyleActionSheet];

UIPopoverPresentationController* presenter = actionSheet.popoverPresentationController;

// chosen anchor UIBarButtonItem
presenter.barButtonItem = anchorButton;

// disabled UIViewController buttons
presenter.managedBarButtonItems = self.toolbarItems;

也可能:

// disabled UINavigationController buttons
presenter.managedBarButtonItems =
    [[NSArray arrayWithArray:self.navigationItem.leftBarButtonItems]
        arrayByAddingObject:self.navigationItem.rightBarButtonItem];