2012-01-05 24 views
19

Tôi đang tạo bố cục tùy chỉnh của cửa sổ bật lên mà iOS cung cấp. Tôi đã phân lớp UIPopoverBackgroundView và nhận nó để vẽ nền cho cửa sổ bật lên của tôi một cách chính xác. Vấn đề của tôi bây giờ là UIPopoverController tạo ra một bóng bên trong trên cửa sổ bật lên ảnh hưởng đến contentViewController của cửa sổ bật lên. Tôi muốn loại bỏ bóng bên trong này, vì vậy chỉ có nội dung của contentViewController của tôi được hiển thị.Xóa bóng bên trong mà UIPopoverController tạo ra

Đây là cách cửa sổ bật lên hiện có, với UILabel để minh họa hiệu ứng trên contentViewController.

popover

Có cách nào để xóa bóng bên trong này không?

+0

Xem câu trả lời của kajham, cho iOS 6 phải là câu trả lời được chấp nhận. –

Trả lời

25

Hỗ trợ cho việc này đã được bổ sung trong ios6.0 với các cuộc gọi sau đây:

+ (BOOL)wantsDefaultContentAppearance

Liên kết đến tài liệu: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPopoverBackgroundView_class/Reference/Reference.html

+0

Đây phải là câu trả lời được chấp nhận. –

+4

điều này không hiệu quả đối với tôi .. Tôi có một lớp con của UIPopoverBackgroundView, đã ghi đè phương thức và không có gì. bóng ảnh tĩnh ở đó .. bất kỳ trợ giúp nào ?? xin vui lòng – Frade

+0

@Frade bạn cần phải ghi đè lên và không gọi 'super' trong' - (void) layoutSubviews' phương pháp (tôi tìm thấy nó ở đây: http://stackoverflow.com/questions/10044227/custom-uipopoverbackgroundview-no-drop -shadow # comment27674273_12684634) – derpoliuk

1

Tôi không tin rằng có một cách thanh lịch/được hỗ trợ để đạt được điều đó bằng cách sử dụng UIPopover chuẩn của Apple. Tuy nhiên, bạn có thể làm cho lớp popover tùy chỉnh của riêng bạn khá dễ dàng. Có khá một vài ví dụ về làm thế nào để làm như vậy cả ở đây trên SO và hướng dẫn trên web rộng hơn (thậm chí một vài giải pháp sẵn sàng để tải về). Chỉ cần đặt 'uipopover tùy chỉnh' vào Google ...

+0

Vâng. Tôi chỉ nghĩ rằng nó sẽ được tốt đẹp để có thể thực sự tùy chỉnh sự xuất hiện của UIPopover. Kể từ khi Apple giới thiệu một số khả năng tùy chỉnh với UIPopoverBackgroundView, tôi chỉ nghĩ rằng có thể thực sự thay đổi cách nhìn của UIPopover. – Sorig

+0

Đây không phải là trường hợp. Thực hiện lại các cửa sổ bật lên khắp nơi là một ý tưởng tồi. –

+1

@BenLachman Care để cho chúng tôi biết lý do tại sao? Câu hỏi không nói bất cứ điều gì về "khắp nơi". Thay vào đó, anh mô tả những gì anh cần, và hỏi liệu một đối tượng UIKit có phù hợp với nhu cầu hay không. Vào thời điểm đó thì không. Có hoàn toàn không có lý do tại sao cán yếu tố của riêng bạn trong trường hợp này là một ý tưởng tồi trong-và-của-chính nó. – isaac

10

Vì không có cách nào thanh lịch để làm điều này và vì tôi không muốn viết lại toàn bộ UIPopoverController chỉ để thực hiện việc này, tôi đã tạo một hack đơn giản xóa bóng bên trong trên cửa sổ bật lên bằng cách duyệt qua cấu trúc UIView. Các hack là một thể loại trên UIPopoverController và tôi chỉ cần đặt nó trong các tập tin cho lớp con của tôi của UIPopoverBackgroundView. Vì vậy, đây là các mã:

@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 

Khi tôi muốn hiển thị popover của tôi, tôi chỉ cần gọi presentPopoverWithoutInnerShadowFromRect:presentPopoverWithoutInnerShadowFromBarButtonItem: phương pháp thay vì những tiêu chuẩn. Chú ý: Hãy nhớ #import <QuartzCore/QuartzCore.h> cho mã để làm việc

+0

Nếu tôi có UITableView bên trong cửa sổ bật lên, mặt nạ góc xuất hiện khi cuộn. Bất kỳ ý tưởng nếu có một cách để làm việc xung quanh này? – bromanko

+0

Bạn có thể sử dụng "removeInnerShadow" để xóa mặt nạ góc và bóng của cửa sổ bật lên. Hãy thử chơi với nó. Bạn có thể thử và gọi "removeInnerShadow" liên tục trong khi cuộn hoặc có thể bạn có thể tìm cách ngăn việc di chuyển thêm các mặt nạ góc một lần nữa. – Sorig

+0

Biến ra cũng thiết lập màu nền đã giúp: 'subviewA.layer.backgroundColor = [UIColor clearColor] .CGColor;' – bromanko

10

tôi juest đã tạo phiên bản của riêng tôi cho dự án tôi đang làm việc.

Về cơ bản bạn nên sử dụng tùy chỉnh của riêng bạn backgroundClass cho popover và trong lớp học này, bạn cần xác định:

- (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; 
                   } 
               } 
           } 
       } 
   } 
} 

nên được khá nhiều đạn để cập nhật iOS.

+1

Tính năng này hoạt động tốt với popOvers cho chế độ xem, không được nhúng trong NavigationControllers. Ngay sau khi ViewController (được nhúng trong NavigationController) được sử dụng cho cửa sổ bật lên thì bóng sẽ quay trở lại. Có giải pháp nào cho điều này không? – FrankZp

+0

#FrankZP Hãy tìm sửa đổi bên dưới. –

+0

+1 giải pháp tuyệt vời! Một câu hỏi: bạn có thể giải thích một chút tại sao gửi một thông điệp tới '[self class]' (đó là một 'objc_class') làm việc? – matm

0

Đối FrankZp

này hoạt động tốt với popOvers cho quan điểm, không được nhúng trong NavigationControllers. Ngay sau khi ViewController (được nhúng trong NavigationController) được sử dụng cho cửa sổ bật lên, bóng sẽ trở lại. Có giải pháp nào cho điều này không?

Đây là sửa đổi cho 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]; 
           } 
          } 
         } 
        } 
       } 
      } 
     } 
    } 
} 
3

Mặc dù tôi đồng ý về nguyên tắc rằng cách thích hợp để xử lý này là để cuộn Popover của riêng bạn, trong trường hợp này đó là một vấn đề không cho các phiên bản mới hơn của hệ điều hành. Tôi có thực sự muốn xây dựng và duy trì triển khai cửa sổ bật lên của riêng mình chỉ để hỗ trợ một hệ điều hành mà cuối cùng sẽ không liên quan? Nếu bạn thực sự muốn, hãy xem xét một số triển khai mã nguồn mở miễn phí trên web.

Cá nhân tôi đã nghiên cứu các phương pháp được đề xuất ở đây và đã tự mình sử dụng trang này làm điểm bắt đầu (cảm ơn!). Nó hoạt động cho cả hai kịch bản (có hoặc không có thanh điều hướng) và an toàn hơn một chút theo ý kiến ​​của tôi.

Thay vì thêm phương thức vào UIPopoverController, tôi đã thêm một thường trình vào UIPopoverBackgroundView của mình để tìm kiếm các khung nhìn vi phạm bằng cách sử dụng một tuyến RELATIVE, chứ không phải là ABSOLUTE. Trong ngắn hạn, kể từ khi mã có một tham chiếu trực tiếp đến UIPopoverBackgroundView (tự), nó có thể điều hướng lên (superview) và sau đó xuống (subviews).

Các cây nhìn giống như thế này trong cả hai kịch bản:

Với UINavigationBar: enter image description here

Without UINavigationBar: enter image description here

Hai quan điểm rằng chúng ta quan tâm là Chế độ xem UILayoutView và UIImage được in đậm và gạch chân trong mỗi biểu đồ. Chúng ta có thể tham chiếu đến những thứ này bắt đầu từ UIPopoverBackgroundView bằng cách sử dụng đoạn mã dưới đây (giả sử ARC). Tôi thực hiện điều này từ layoutSubviews trong triển khai UIPopoverBackgroundView của mình.

// 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]; 

Tôi sử dụng khối ở đây để thực hiện tra cứu các bản phụ để giữ mã ngắn gọn. Nó có một khung nhìn như là một điểm khởi đầu và một mảng các tên lớp. Mảng các tên lớp là chuỗi các lớp khung nhìn mà tôi mong đợi trong đó chỉ mục 0 là phụ huynh của chỉ mục 1 và chỉ mục 1 là phụ huynh của chỉ mục 2, vv. Nó trả về khung nhìn được biểu diễn bởi mục cuối cùng trong mảng.

+0

Bạn nên thêm điều kiện để tránh thực hiện bất kỳ điều nào trong số này trong iOS 6+ và chỉ trả về 'NO' từ' wantDefaultContentAppearance'. –

+0

+1 để có giải thích sơ đồ chi tiết. Bạn cũng có thể cho biết cách điều chỉnh kích thước đường viền của UIPopover không? – HDdeveloper

+0

Bạn có nghĩa là biên giới bên ngoài? Tôi đã sử dụng UIPopoverBackgroundView để sử dụng hình ảnh để chỉ định giao diện của cửa sổ bật lên. Nếu bạn có nghĩa là biên giới bên trong, tôi chỉ cần loại bỏ nó. Việc triển khai mặc định sử dụng UIImageView vì vậy bạn phải thay thế hình ảnh nếu bạn chỉ muốn thay đổi đường viền. –

Các vấn đề liên quan