Удалите внутреннюю тень, создаваемую UIPopoverController.
Я создаю пользовательский макет popover, который предоставляет iOS. Я подклассифицировал UIPopoverBackgroundView и получил его, чтобы правильно рисовать фон для моего popover. Моя проблема в том, что UIPopoverController создает внутреннюю тень на popover, влияющую на contentViewController popover. Я хочу удалить эту внутреннюю тень, поэтому отображается только контент моего ContentViewController.
Вот как выглядит popover в настоящее время, с UILabel, чтобы продемонстрировать влияние на ContentViewController.
![popover]()
Есть ли способ удалить эту внутреннюю тень?
Ответы
Ответ 1
Поддержка этого была добавлена в ios6.0 со следующим вызовом:
+ (BOOL)wantsDefaultContentAppearance
Ссылка на документацию:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPopoverBackgroundView_class/Reference/Reference.html
Ответ 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];
}
}
}
}
}
}
}
}
}