[英]How do I prevent UIPopoverController passthroughViews from being reset after orientation change when presented from a UIBarButtonItem?
I have a UIPopoverController which is being presented from a UIBarButtonItem. 我有一个UIPopoverController,它是从UIBarButtonItem呈现的。 I want touches outside of the popover to dismiss the popover. 我想在popover之外触摸以解除popover。 When presenting a popover from a bar button, the other bar buttons are automatically included in the popovers passthrough views. 当从条形按钮呈现弹出框时,其他条形按钮将自动包含在弹出框直通视图中。 In order to prevent that I set the passthrough views to nil (or @[ ]) after presenting the popover, like so: 为了防止我在呈现弹出窗口后将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;
}
That's all fine and good, but the problem that I'm having is that after rotating the device while the popover is visible the bar buttons are being automatically re-added as passthrough views and don't cause the popover to be dismissed . 这一切都很好,但我遇到的问题是,在弹出设备后,当弹出窗口可见时,条形按钮会自动重新添加为直通视图,并且不会导致弹出窗口被解除 。
If I could somehow get the bar buttons view (or rect) then I could present the popover using 如果我能以某种方式获得条形按钮视图(或矩形),那么我可以使用弹出窗口
-presentPopoverFromRect:inView:permittedArrowDirections:animated:
which would likely fix this problem, but I don't know of any non-hackish way of finding that rect/view from a UIBarButtonItem. 这可能会解决这个问题,但我不知道从UIBarButtonItem找到rect / view的任何非hackish方式。
I really don't want the selectors called when the other bar buttons are hit to dismiss the popover programmatically, that's not their responsibility and will likely cause problems for me later. 我真的不希望当其他按钮被按下时调用的选择器以编程方式关闭弹出窗口,这不是他们的责任,并且可能会在以后引起我的问题。
Any ideas? 有任何想法吗?
So I came up with a solution, that's a little odd, but keeps things modular, works well. 所以我提出了一个解决方案,这有点奇怪,但保持模块化,运行良好。 I've created a class called PropertyEnforcer which registers itself as a KVO observer of an object's property and re-sets that property any time it changes. 我创建了一个名为PropertyEnforcer的类,它将自己注册为对象属性的KVO观察者,并在其更改时重新设置该属性。
PropertyEnforcer.h: PropertyEnforcer.h:
#import <Foundation/Foundation.h>
@interface PropertyEnforcer : NSObject
+ (void) enforceProperty:(NSString*)keyPath ofObject:(id)target toValue:(id)value;
@end
PropertyEnforcer.m: 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
Now I can change the change the original code to: 现在我可以将原始代码的更改更改为:
- (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];
}
The PropertyEnforcer registers itself as an associated object so we don't ever have to keep track of it. PropertyEnforcer将自身注册为关联对象,因此我们无需跟踪它。 It will automatically unregister itself as a KVO observer and be destroyed whenever the UIPopoverController is destroyed. 它会自动注销自己作为KVO观察者,并在UIPopoverController被销毁时被销毁。
This is the best, least hackish, solution that I could come up with. 这是我能提出的最好,最不具有解决方案的解决方案。
The solution I went for was to leave passthroughViews
alone and instead disable/re-enable individual buttons ( UIBarButtonItem
instances) in a toolbar or a navigation bar when UIPopoverPresentationController
is presented and dismissed, based on its transition. 我使用的解决方案是单独保留passthroughViews
,而是在UIPopoverPresentationController
根据其转换呈现和解除时,禁用/重新启用工具栏或导航栏中的各个按钮( UIBarButtonItem
实例)。
(iOS 8: UIPopoverPresentationController
instead of UIPopoverController
.) (iOS 8: UIPopoverPresentationController
而不是UIPopoverController
。)
UIPopoverPresentationController+managedBarButtonItems.h UIPopoverPresentationController + managedBarButtonItems.h
@interface UIPopoverPresentationController (managedBarButtonItems)
@property (nonatomic, retain) NSArray* managedBarButtonItems;
@end
UIPopoverPresentationController+managedBarButtonItems.m 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
Usage: 用法:
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;
Also possible: 也可能:
// disabled UINavigationController buttons
presenter.managedBarButtonItems =
[[NSArray arrayWithArray:self.navigationItem.leftBarButtonItems]
arrayByAddingObject:self.navigationItem.rightBarButtonItem];
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.