简体   繁体   English

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

[英]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.

相关问题 防止在出现警报时显示视图控制器 - Prevent view controllers from being presented when alert is there 如何防止在出现警报时显示视图控制器的 inputAccessoryView? - How to prevent inputAccessoryView of a view controller from being shown when an alert is presented? 如何在UIPopoverController(iOS7)中防止UITableView分隔符颜色变黑? - How do I prevent UITableView separator color from turning black in UIPopoverController (iOS7)? 如何从弹出窗口中显示UIViewController中找到UIPopoverController? - How can I find the UIPopoverController from the UIViewController being displayed in a popover? 如何防止用户点击Popover之外的其他UIView(UIBarButtonItem)? - How do I prevent the user from tapping other UIViews (UIBarButtonItem) outside a Popover? UIPopoverController-出现时的SIGABRT - UIPopoverController - SIGABRT when Presented 从纵向旋转到横向时如何更改UIBarButtonItem的大小? - how to change the UIBarButtonItem size when rotate from the portrait to landscape? 如何防止UITableViewCell被选择? - How do I prevent a UITableViewCell from being selectable? 在 Swift 中,如何防止函数在子类上被调用? - In Swift, how do I prevent a function from being called on a subclass? 如何在Swift中更改UIBarButtonItem的字体? - How do I change the font of a UIBarButtonItem in Swift?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM