Подтвердить что ты не робот

Удалите внутреннюю тень, создаваемую UIPopoverController.

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

Вот как выглядит popover в настоящее время, с UILabel, чтобы продемонстрировать влияние на ContentViewController.

popover

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

4b9b3361

Ответ 2

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

Когда я хочу отображать мой popover, я просто вызываю методы presentPopoverWithoutInnerShadowFromRect: и presentPopoverWithoutInnerShadowFromBarButtonItem: вместо стандартных. ПРИМЕЧАНИЕ. Не забудьте #import <QuartzCore/QuartzCore.h>, чтобы код работал

Ответ 3

I juest создал мою собственную версию для проекта, над которым я работаю.

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

Ответ 4

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

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

Вместо добавления метода в UIPopoverController я добавил подпрограмму в свой UIPopoverBackgroundView, чтобы искать нарушающие представления, используя маршрут RELATIVE, а не ABSOLUTE. Короче говоря, поскольку код имеет прямую ссылку на UIPopoverBackgroundView (self), он может перемещаться вверх (супервизор), а затем вниз (subviews).

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

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

Ответ 5

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

Ответ 6

Для FrankZp

Это отлично работает с popOvers для представлений, которые не встроены в NavigationControllers. Как только 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];
                                }
                            }
                        }
                    }
                }
            }
        } 
    }
}