【问题标题】:How do I prevent UIPopoverController passthroughViews from being reset after orientation change when presented from a UIBarButtonItem?从 UIBarButtonItem 呈现方向更改后,如何防止 UIPopoverController passthroughViews 被重置?
【发布时间】:2014-06-07 15:50:27
【问题描述】:

我有一个从 UIBarButtonItem 呈现的 UIPopoverController。我想在弹出框之外进行触摸以关闭弹出框。从条形按钮呈现弹出框时,其他条形按钮会自动包含在弹出框传递视图中。为了防止我在显示弹出框后将直通视图设置为 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 中找到该矩形/视图。

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

有什么想法吗?

【问题讨论】:

    标签: ios uipopovercontroller uibarbuttonitem


    【解决方案1】:

    所以我想出了一个解决方案,这有点奇怪,但保持模块化,效果很好。我创建了一个名为 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 被销毁时被销毁。

    这是我能想到的最好的、最不老套的解决方案。

    【讨论】:

      【解决方案2】:

      我采用的解决方案是单独留下 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 <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];
      

      【讨论】:

        猜你喜欢
        • 2012-06-25
        • 1970-01-01
        • 2012-01-11
        • 1970-01-01
        • 1970-01-01
        • 2020-07-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多