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

我有一个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方式。

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

有任何想法吗?

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

PropertyEnforcer.h:

 #import  @interface PropertyEnforcer : NSObject + (void) enforceProperty:(NSString*)keyPath ofObject:(id)target toValue:(id)value; @end 

PropertyEnforcer.m:

 #import "PropertyEnforcer.h" #import  @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被销毁时被销毁。

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

我采用的解决方案是单独保留passthroughViews ,而是在UIPopoverPresentationController根据其转换呈现和解除时,禁用/重新启用工具栏或导航栏中的各个按钮( UIBarButtonItem实例)。

(iOS 8: UIPopoverPresentationController而不是UIPopoverController 。)

UIPopoverPresentationController + managedBarButtonItems.h

 @interface UIPopoverPresentationController (managedBarButtonItems) @property (nonatomic, retain) NSArray* managedBarButtonItems; @end 

UIPopoverPresentationController + managedBarButtonItems.m

 #import "UIPopoverPresentationController+managedBarButtonItems.h" #import  // // 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];