Удалите внутреннюю тень, которую создает UIPopoverController - PullRequest
19 голосов
/ 05 января 2012

Я создаю пользовательский макет поповера, который предоставляет iOS. Я подклассифицировал UIPopoverBackgroundView и заставил его правильно рисовать фон для моего поповера. Моя проблема теперь в том, что UIPopoverController создает внутреннюю тень на всплывающем окне, влияя на contentViewController всплывающего окна. Я хочу удалить эту внутреннюю тень, чтобы отображалось только содержимое моего contentViewController.

Вот так выглядит поповер в настоящее время с UILabel для демонстрации эффекта на contentViewController.

popover

Есть ли способ убрать эту внутреннюю тень?

Ответы [ 6 ]

25 голосов
/ 02 октября 2012

Поддержка этого была добавлена ​​в ios6.0 со следующим вызовом:

+ (BOOL)wantsDefaultContentAppearance

Ссылка на документацию: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPopoverBackgroundView_class/Reference/Reference.html

10 голосов
/ 12 октября 2012

Я только что создал свою версию для проекта, над которым я работаю.

Обычно вы должны использовать свой собственный 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.

10 голосов
/ 31 января 2012

Поскольку нет элегантного способа сделать это, и поскольку я не хочу переписывать весь UIPopoverController просто для этого, я создал простой хак, который удаляет внутреннюю тень на поповере, обходя структуру UIView. Хак это категория на 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>, чтобы код работал

3 голосов
/ 25 февраля 2013

Хотя я согласен в принципе, что правильный способ справиться с этим - запустить собственный Popover, в этом случае это не проблема для более новых версий ОС.Действительно ли я хочу создавать и поддерживать свою собственную реализацию popover только для поддержки ОС, которая в конечном итоге не будет иметь значения?Если вы действительно хотите, рассмотрите некоторые из бесплатных реализаций с открытым исходным кодом в сети.

Лично я исследовал предложенные здесь методы и придумал свой собственный, используя эту страницу в качестве отправной точки (спасибо!).Он работает для обоих сценариев (с панелью навигации или без нее) и, на мой взгляд, немного безопаснее.

Вместо добавления метода в UIPopoverController, я добавил подпрограмму в свой UIPopoverBackgroundView для поиска оскорбительных представлений.используя ОТНОСИТЕЛЬНЫЙ маршрут, а не АБСОЛЮТНЫЙ.Короче говоря, поскольку код имеет прямую ссылку на UIPopoverBackgroundView (self), он может перемещаться вверх (суперпредставление) и затем вниз (подпредставления).

Деревья представлений выглядят так в обоих сценариях:

С UINavigationBar: enter image description here

Без UINavigationBar: enter image description here

Два вида, которые нас интересуют, - это представления UILayoutView и UIImage, которые выделены жирным шрифтом и подчеркнуты на каждом графике.Мы можем получить ссылку на них, начиная с UIPopoverBackgroundView, используя код ниже (предполагает ARC).Я выполняю это из layoutSubviews в моей реализации UIPopoverBackgroundView.

// 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 и т. Д. Он возвращает представление, представленное последним элементом в массиве.

1 голос
/ 05 января 2012

Я не верю, что есть элегантный / поддерживаемый способ добиться этого с помощью стандартного UIPopover от Apple.Тем не менее, вы можете довольно легко создать свой собственный класс popover.Здесь довольно много примеров того, как это сделать, как в SO, так и в учебных руководствах по более широкой сети (даже несколько готовых для загрузки решений).Просто поместите «пользовательский uipopover» в Google ...

0 голосов
/ 29 декабря 2012

Для FrankZp

Это прекрасно работает с popOvers для представлений, которые не встроены в NavigationControllers. Как только ViewController (который является встроенный в NavigationController) используется для всплывающего тень снова вернулась. Есть ли какое-то решение для этого?

* * 1010

Вот модификация для 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];
                                }
                            }
                        }
                    }
                }
            }
        } 
    }
}
...