删除UIPopoverController创建的内部阴影

时间:2012-01-05 15:16:16

标签: objective-c ipad cocoa-touch ios5 uipopovercontroller

我正在创建iOS提供的popover的自定义布局。我已经将UIPopoverBackgroundView子类化,并让它为我的popover正确绘制背景。我的问题是现在UIPopoverController在popover上创建了一个内部阴影,影响了popover的contentViewController。我想删除这个内部阴影,所以只显示我的contentViewController的内容。

这是popover当前的外观,使用UILabel来演示对contentViewController的影响。

popover

有没有办法删除这个内部阴影?

6 个答案:

答案 0 :(得分:25)

在ios6.0中添加了对此的支持,并进行了以下调用:

+ (BOOL)wantsDefaultContentAppearance

文档链接: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPopoverBackgroundView_class/Reference/Reference.html

答案 1 :(得分:10)

由于没有优雅的方法可以做到这一点,因为我不想重写整个UIPopoverController只是为了做到这一点,我创建了一个简单的hack,通过遍历UIView结构来删除popover上的内部阴影。 hack是UIPopoverController上的一个类别,我只是把它放在我的UIPopoverBackgroundView子类的文件中。所以这是代码:

@interface UIPopoverController(removeInnerShadow)

- (void)removeInnerShadow;
- (void)presentPopoverWithoutInnerShadowFromRect:(CGRect)rect 
                                          inView:(UIView *)view 
                        permittedArrowDirections:(UIPopoverArrowDirection)direction 
                                        animated:(BOOL)animated;

- (void)presentPopoverWithoutInnerShadowFromBarButtonItem:(UIBarButtonItem *)item 
                                 permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections 
                                                 animated:(BOOL)animated;

@end

@implementation UIPopoverController(removeInnerShadow)

- (void)presentPopoverWithoutInnerShadowFromRect:(CGRect)rect inView:(UIView *)view permittedArrowDirections:(UIPopoverArrowDirection)direction animated:(BOOL)animated 
{
    [self presentPopoverFromRect:rect inView:view permittedArrowDirections:direction animated:animated];
    [self removeInnerShadow];
}

- (void)presentPopoverWithoutInnerShadowFromBarButtonItem:(UIBarButtonItem *)item 
                                 permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections 
                                                 animated:(BOOL)animated
{
    [self presentPopoverFromBarButtonItem:item permittedArrowDirections:arrowDirections animated:animated];
    [self removeInnerShadow];
}

- (void)removeInnerShadow
{
    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];

    for (UIView *windowSubView in window.subviews)
    {
        if ([NSStringFromClass([windowSubView class]) isEqualToString:@"UIDimmingView"])
        {
            for (UIView *dimmingViewSubviews in windowSubView.subviews)
            {
                for (UIView *popoverSubview in dimmingViewSubviews.subviews)
                {
                    if([NSStringFromClass([popoverSubview class]) isEqualToString:@"UIView"]) 
                    {
                        for (UIView *subviewA in popoverSubview.subviews)
                        {
                            if ([NSStringFromClass([subviewA class]) isEqualToString:@"UILayoutContainerView"])
                            {
                                subviewA.layer.cornerRadius = 0;
                            }

                            for (UIView *subviewB in subviewA.subviews)
                            {
                                if ([NSStringFromClass([subviewB class]) isEqualToString:@"UIImageView"] )
                                {
                                    [subviewB removeFromSuperview];
                                }
                            }
                        }
                    }
                }
            }
        } 
    }
}

@end

当我想显示我的popover时,我只需调用presentPopoverWithoutInnerShadowFromRect:presentPopoverWithoutInnerShadowFromBarButtonItem:方法而不是标准方法。 注意:请记住#import <QuartzCore/QuartzCore.h>以使代码生效

答案 2 :(得分:10)

我为我正在进行的项目创建了自己的版本。

基本上你应该使用自己的自定义backgroundClass进行弹出,在这个类中你应该定义:

- (void)willMoveToWindow:(UIWindow *)newWindow {
    [super willMoveToWindow:newWindow];
    if ([UIPopoverBackgroundView respondsToSelector:@selector(wantsDefaultContentAppearance)]) 
        return;

    if (![[self class] wantsDefaultContentAppearance]) {
        for (UIView *view in self.superview.subviews) {
            for (UIView *subview in view.subviews) {
                if (subview.layer.cornerRadius != 0.0) {
                   subview.layer.cornerRadius = 0.0;
                    for (UIView *subsubview in subview.subviews) {
                        if (subsubview.class == [UIImageView class])
                            subsubview.hidden = YES;
                    }
                }
            }
        }
    }
}

对于iOS更新来说应该是非常防弹的。

答案 3 :(得分:3)

虽然我在校长方面同意处理此问题的正确方法是滚动自己的Popover,但在这种情况下,对于较新版本的操作系统来说,这不是问题。我是否真的只想构建和维护自己的popover实现,以支持最终无关的操作系统?如果您真的想要,请考虑网上的一些免费开源实现。

就个人而言,我调查了这里建议的方法,并以此页面为出发点(谢谢!)。它适用于两种情况(有或没有导航栏),在我看来更安全。

我没有向UIPopoverController添加方法,而是向我的UIPopoverBackgroundView添加了一个例程,以使用RELATIVE路由而不是ABSOLUTE来查找有问题的视图。简而言之,由于代码直接引用了UIPopoverBackgroundView(self),因此它可以向上导航(superview)然后向下导航(subviews)。

在两种情况下,视图树都是这样的:

使用UINavigationBar: enter image description here

没有UINavigationBar: enter image description here

我们感兴趣的两个视图是每个图中粗体和下划线的UILayoutView和UIImage视图。我们可以使用下面的代码(假设为ARC)从UIPopoverBackgroundView开始引用这些引用。我在我的UIPopoverBackgroundView实现中从layoutSubviews执行此操作。

// Helper method for traversing child views based solely on class types
UIView* (^__unsafe_unretained __block traverseSubviews)(UIView*, NSArray*) = ^(UIView *root, NSArray* nodeTypes) {
    NSString *typeName = [nodeTypes objectAtIndex:0];
    for (UIView *subView in root.subviews) {
        if ([NSStringFromClass([subView class]) isEqualToString: typeName]) {
            if (nodeTypes.count == 1)
                return subView;
            else
                return traverseSubviews(subView, [nodeTypes subarrayWithRange:NSMakeRange(1, nodeTypes.count - 1)]);
        }
    }
    return (UIView*)nil;
};

// Find the subviews of interest, taking into account there could be a navigation bar
UIView *layoutView = traverseSubviews([self superview], @[@"UIView", @"UILayoutContainerView"]);
if (traverseSubviews(layoutView, @[@"UINavigationBar"])) {
    layoutView = traverseSubviews(layoutView, @[@"UILayoutContainerView"]);
}
UIView *imageView = traverseSubviews(layoutView, @[@"UIImageView"]);

// Remove the default content appearance
layoutView.layer.cornerRadius = 0;
[imageView removeFromSuperview];

我在这里使用一个块来遍历子视图以保持代码简洁。它将视图作为起点和类名数组。类名数组是我期望的视图类序列,其中索引0是索引1的父类,索引1是索引2的父类,等等。它返回数组中最后一项表示的视图。

答案 4 :(得分:1)

我不相信使用Apple的标准UIPopover可以实现优雅/支持的方式。但是,您可以非常轻松地创建自己的自定义popover类。这里有很多关于如何在SO和更广泛的网络上的教程(甚至一些现成的下载解决方案)这样做的例子。只需将“自定义uipopover”放入Google即可...

答案 5 :(得分:0)

FrankZp

  

这适用于没有嵌入的视图的popOvers   NavigationControllers。一旦ViewController(它是   嵌入在NavigationController中)用于popover   阴影又回来了。这有什么解决方案吗?

以下是UINavigationController的修改:

- (void)removeInnerShadow
{
    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];

    for (UIView *windowSubView in window.subviews) {
        if ([NSStringFromClass([windowSubView class]) isEqualToString:@"UIDimmingView"] {
            for (UIView *dimmingViewSubviews in windowSubView.subviews) {
                for (UIView *popoverSubview in dimmingViewSubviews.subviews) {
                    if([NSStringFromClass([popoverSubview class]) isEqualToString:@"UIView"]) {
                        for (UIView *subviewA in popoverSubview.subviews) {
                            if ([NSStringFromClass([subviewA class]) isEqualToString:@"UILayoutContainerView"]) {
                                subviewA.layer.cornerRadius = 0;
                            }

                            for (UIView *subviewB in subviewA.subviews) {
                                if ([NSStringFromClass([subviewB class]) isEqualToString:@"UILayoutContainerView"]) {
                                    for (UIView * subviewC in subviewB.subviews) {
                                        if ([NSStringFromClass([subviewC class]) isEqualToString:@"UIImageView"] ) {
                                            [subviewC removeFromSuperview];
                                        }
                                    }
                                }

                                if ([NSStringFromClass([subviewB class]) isEqualToString:@"UIImageView"] ) {
                                    [subviewB removeFromSuperview];
                                }
                            }
                        }
                    }
                }
            }
        } 
    }
}