【问题标题】:Remove the inner shadow that UIPopoverController creates移除 UIPopoverController 创建的内阴影
【发布时间】:2012-02-03 09:56:50
【问题描述】:

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

这是弹出框当前的样子,用 UILabel 来演示 contentViewController 上的效果。

有什么办法可以去掉这个内阴影?

【问题讨论】:

  • 看 kajham 的答案,因为 iOS 6 应该是公认的答案。

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


【解决方案1】:

在 ios6.0 中通过以下调用添加了对此的支持:

+ (BOOL)wantsDefaultContentAppearance

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

【讨论】:

  • 这对我不起作用..我有一个 UIPopoverBackgroundView 的子类,重写了该方法,但什么也没有。那里的阴影仍然存在..有什么帮助吗??请
  • @Frade 你需要重写并且不要在-(void)layoutSubviews 方法中调用super(我在这里找到它:stackoverflow.com/questions/10044227/…
  • 老实说,我不记得我需要这个的目的是什么。无论如何,谢谢! ;)
【解决方案2】:

由于没有优雅的方法可以做到这一点,而且我不想重写整个 UIPopoverController 只是为了做到这一点,我创建了一个简单的 hack,通过遍历 UIView 结构来移除弹出框上的内部阴影。 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

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

【讨论】:

  • 如果我在弹出框内有一个 UITableView,滚动时会出现角掩码。知道是否有办法解决这个问题吗?
  • 您可以使用“removeInnerShadow”来移除弹出框的角遮罩和阴影。试着玩弄它。您可以尝试在滚动时连续调用“removeInnerShadow”,或者您可以寻找一种方法来阻止滚动再次添加角掩码。
  • 原来设置背景颜色也有帮助:subviewA.layer.backgroundColor = [UIColor clearColor].CGColor;
  • 这是一个相当脆弱的 hack。这个问题的正确答案应该是isaac's。自己动手。
【解决方案3】:

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

基本上你应该使用你自己的自定义backgroundClass 来实现popover,并且你应该在这个类中定义:

- (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 更新应该是防弹的。

【讨论】:

  • 这适用于视图的 popOvers,它没有嵌入到 NavigationControllers 中。一旦 ViewController(嵌入在 NavigationController 中)用于弹出框,阴影就会再次出现。有什么解决办法吗?
  • #FrankZP 寻找下面的修改。
  • +1 不错的解决方法!一个问题:你能解释一下为什么向[self class](这是objc_class)发送消息有效吗?
【解决方案4】:

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

我个人研究了此处建议的方法,并以此页面为起点提出了自己的方法(谢谢!)。它适用于两种情况(有或没有导航栏),并且在我看来更安全。

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

两种情况下的视图树看起来都是这样的:

使用 UINavigationBar:

没有 UINavigationBar:

我们感兴趣的两个视图是 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 的父级,依此类推。它返回数组中最后一项表示的视图。

【讨论】:

  • 您应该添加一个条件以避免在 iOS 6+ 中执行任何此类操作,只需从 wantsDefaultContentAppearance 返回 NO
  • +1 以获得详细的图解说明。你能告诉我如何调整 UIPopover 的边框大小吗?
  • 你的意思是外边框吗?我使用 UIPopoverBackgroundView 来使用图像来指定弹出框的外观。如果您指的是内部边框,我只是将其删除。默认实现使用 UIImageView,因此如果您只想更改边框,则必须替换图像。
【解决方案5】:

我不相信有一种优雅/受支持的方式可以使用 Apple 的标准 UIPopover 来实现这一目标。但是,您可以相当轻松地制作自己的自定义弹出框类。在 SO 和更广泛的网络上的教程(甚至一些准备下载的解决方案)上都有很多关于如何做到这一点的示例。只需将“自定义 uipopover”放入 Google...

【讨论】:

  • 是的,好的。我只是认为能够真正自定义 UIPopover 的外观会很好。由于 Apple 为 UIPopoverBackgroundView 引入了一些自定义功能,我只是认为实际上可以改变 UIPopover 的外观。
  • 事实并非如此。在所有地方重新实现弹出框是个坏主意。
  • @BenLachman 想告诉我们为什么?这个问题没有提到“到处都是”。相反,他描述了他的需求,并询问 UIKit 对象是否适合需求。当时并没有。在这种情况下,完全没有理由滚动自己的元素本身就是一个坏主意。
  • @issac 我不能说我同意。通常,对“这不会完全按照我想要的方式工作”的第一个响应应该是“好吧,在这种情况下,我为什么要以自定义方式执行此操作?”而不是“好吧,让我们花点时间推出一个几乎可以肯定不如 Apple 强大的定制解决方案”。滚动自己的东西绝对是可行的,但应该是一个经过深思熟虑的决定,而不是本能的反应。
  • @BenLachman 您的 cmets 完全是主观的 - 花在重新创建功能上的时间是一个完全合理的问题,但它不是不做某事的技术原因 - 同样使用其他人的糟糕示例作为不做的理由自己的东西。我同意有些课程的行为可能不是由那些没有丰富经验或动机的人最好地重新创建。 UIPopover 不是其中之一。
【解决方案6】:

对于 FrankZp

这适用于未嵌入视图的 popOvers 导航控制器。只要 ViewController(即 嵌入在 NavigationController 中)用于弹出框 影子又回来了。有什么解决办法吗?

这是 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];
                                }
                            }
                        }
                    }
                }
            }
        } 
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    • 2011-03-05
    • 2014-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多