preferStatusBarStyle không được gọi


257

Tôi đã theo chủ đề này để ghi đè -preferredStatusBarStyle, nhưng nó không được gọi. Có bất kỳ tùy chọn mà tôi có thể thay đổi để kích hoạt nó? (Tôi đang sử dụng XIB trong dự án của mình.)


Nó không được gọi trong bối cảnh nào: giả lập? trên thiết bị?
tạm biệt

@bneely cả hai.
trgoofi

Bạn đang sử dụng trình giả lập iOS 7, thiết bị iOS 7 và SDK cơ sở của bạn là 7.0?
tạm biệt

@bneely iOS SDK 7.0 được hiển thị bên dưới tên dự án của tôi, điều đó có nghĩa là SDK cơ sở của tôi là 7.0?
trgoofi

Trong cài đặt bản dựng, "SDK cơ sở" là nơi đặt giá trị. Có vẻ như dự án của bạn được đặt thành 7.0.
tạm biệt

Câu trả lời:


117

Nguyên nhân gốc rễ có thể

Tôi đã có cùng một vấn đề và nhận ra nó đã xảy ra bởi vì tôi không đặt trình điều khiển xem gốc trong cửa sổ ứng dụng của mình.

Cái UIViewControllermà tôi đã thực hiện preferredStatusBarStyleđược sử dụng trong một UITabBarController, điều khiển sự xuất hiện của các khung nhìn trên màn hình.

Khi tôi đặt bộ điều khiển xem gốc để trỏ đến điều này UITabBarController, các thay đổi trên thanh trạng thái bắt đầu hoạt động chính xác, như mong đợi (và preferredStatusBarStylephương thức đang được gọi).

(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ... // other view controller loading/setup code

    self.window.rootViewController = rootTabBarController;
    [self.window makeKeyAndVisible];
    return YES;
}

Phương pháp thay thế (Không dùng nữa trong iOS 9)

Ngoài ra, bạn có thể gọi một trong các phương pháp sau, nếu phù hợp, trong mỗi bộ điều khiển chế độ xem của bạn, tùy thuộc vào màu nền của nó, thay vì phải sử dụng setNeedsStatusBarAppearanceUpdate:

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

hoặc là

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];

Lưu ý rằng bạn cũng sẽ cần phải thiết lập UIViewControllerBasedStatusBarAppearanceđể NOtrong file plist nếu bạn sử dụng phương pháp này.


2
Tôi có cùng một vấn đề như bạn, không thiết lập trình điều khiển xem gốc. Làm thế quái nào bạn tìm thấy điều đó?
trgoofi

1
Tôi nghi ngờ rằng một cái gì đó trong khung không nhận được thông báo từ setNeedsStatusBarAppearanceUpdate- những nghi ngờ của tôi đã được xác nhận khi tôi thực hiện thay đổi này.
AbdullahC

2
Một vấn đề liên quan mà tôi tìm thấy trong một ứng dụng là bộ điều khiển chế độ xem với bộ điều khiển chế độ xem con toàn màn hình không ghi đè childViewControllForStatusBarStyle và childViewControllForStatusBarHidden để trả về bộ điều khiển xem con đó. Nếu bạn có hệ thống phân cấp trình điều khiển chế độ xem của riêng mình, bạn cần cung cấp các phương thức này để thông báo cho hệ thống sử dụng trình điều khiển chế độ xem nào để xác định kiểu thanh trạng thái.
Jon Steinmetz

thiết lập bộ điều khiển rootview không thay đổi bất cứ điều gì. Bạn nên làm việc với bình luận của Jon. Và hãy cẩn thận khi gọi setneedstatusbarappparentUpdate. Bạn nên gọi nó từ cha mẹ để làm việc.
doozMen

1
@Hippo bạn là thiên tài !! Làm thế nào bạn tìm thấy rằng đó là do không thiết lập bộ điều khiển rootview?
ViruMax

1019

Đối với bất cứ ai sử dụng UINavestionControll:

Các UINavigationControllerkhông mong về preferredStatusBarStylecuộc gọi đến bộ điều khiển xem con của nó. Thay vào đó, nó quản lý trạng thái của chính nó - như thường lệ, nó đang vẽ ở phía trên màn hình nơi thanh trạng thái sống và do đó phải chịu trách nhiệm cho nó. Do đó, việc triển khai preferredStatusBarStylecác VC của bạn trong bộ điều khiển điều hướng sẽ không làm gì cả - chúng sẽ không bao giờ được gọi.

Bí quyết là những gì UINavigationControllersử dụng để quyết định trả lại cho UIStatusBarStyleDefaulthoặc UIStatusBarStyleLightContent. Nó dựa trên cơ sở này UINavigationBar.barStyle. Mặc định ( UIBarStyleDefault) dẫn đến UIStatusBarStyleDefaultthanh trạng thái nền trước tối . Và UIBarStyleBlacksẽ đưa ra một UIStatusBarStyleLightContentthanh trạng thái.

TL; DR:

Nếu bạn muốn UIStatusBarStyleLightContenttrên UINavigationControllersử dụng:

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

59
Đẹp! Lưu ý rằng preferredStatusBarStyletrên thực tế sẽ được gọi trên bộ điều khiển xem con nếu bạn ẩn thanh điều hướng (được đặt navigationBarHiddenthành YES), chính xác khi thích hợp.
Patrick Pijnappel

25
Cảm ơn câu trả lời này. Nếu bạn muốn đặt barStyle cho tất cả các thanh điều hướng của mình, hãy gọi[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
Thomas Desert

15
Câu trả lời hoàn hảo. Không có câu trả lời nào khác trên SO đưa UINavestionControll vào xem xét. 2 tiếng đập đầu vào bàn phím.
Ryan Alford

10
Kudos gửi tới @Patrick vì đã chỉ ra rằng navigationBarHiddenthiết lập YESsẽ thực sự preferredStatusBarStyleđược gọi và một cảnh báo cho những người có thể vấp ngã về điều này: nó hoạt động với navigationBarHidden, nhưng không phải với navigationBar.hidden!
jcaron

4
nên rõ ràng, nhưng bạn cũng cần "Xem giao diện thanh trạng thái dựa trên bộ điều khiển" được đặt thành CÓ trong Info.plist để điều này hoạt động.
Code Baller

99

Vì vậy, tôi thực sự đã thêm một danh mục vào UINavestionControll nhưng đã sử dụng các phương thức:

-(UIViewController *)childViewControllerForStatusBarStyle;
-(UIViewController *)childViewControllerForStatusBarHidden;

và có những cái trả về UIViewControll hiện tại. Điều đó cho phép bộ điều khiển chế độ xem hiển thị hiện tại đặt kiểu / mức độ hiển thị ưa thích của riêng nó.

Đây là một đoạn mã hoàn chỉnh cho nó:

Trong Swift:

extension UINavigationController {

    public override func childViewControllerForStatusBarHidden() -> UIViewController? {
        return self.topViewController
    }

    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return self.topViewController
    }
}

Trong Mục tiêu-C:

@interface UINavigationController (StatusBarStyle)

@end

@implementation UINavigationController (StatusBarStyle)

-(UIViewController *)childViewControllerForStatusBarStyle {
    return self.topViewController;
}

-(UIViewController *)childViewControllerForStatusBarHidden {
    return self.topViewController;
}

@end

Và để có biện pháp tốt, đây là cách nó được triển khai trong UIViewControll:

Trong Swift

override public func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}

override func prefersStatusBarHidden() -> Bool {
    return false
}

Trong Mục tiêu-C

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent; // your own style
}

- (BOOL)prefersStatusBarHidden {
    return NO; // your own visibility code
}

Cuối cùng, hãy đảm bảo rằng ứng dụng của bạn không có "Giao diện thanh trạng thái dựa trên bộ điều khiển" được đặt thành NO. Xóa dòng đó hoặc đặt thành CÓ (mà tôi tin là mặc định cho iOS 7?)


Có vẻ như return self.topViewController;hoạt động đối với tôi, nhưng return self.visibleViewController;- không phải
k06a

viewerViewCont Bộ điều khiển có thể trả về bộ điều khiển phương thức hiện tại khi bạn loại bỏ nó. Đó là bummer. Sử dụng topViewControll.
Ben Sinclair

1
@ d.lebedev ok, nhưng tôi không nghĩ có bất kỳ vấn đề nào được áp dụng ở đây. Bạn không cần phải gọi supertheo phương thức này và bạn thực sự muốn thay đổi hành vi của tất cả các bộ điều khiển loại này
ed '

1
Điều này không hiệu quả với tôi trên iOS 9.3. Tôi đoán, đây là vấn đề: Vấn đề này có ý nghĩa đặc biệt vì nhiều lớp Ca cao được triển khai bằng cách sử dụng các danh mục. Một phương thức xác định khung mà bạn cố gắng ghi đè có thể chính nó đã được triển khai trong một danh mục và do đó việc triển khai nào được ưu tiên không được xác định.
vikingosegundo

2
Điều này là sai và nó bị hỏng trong iOS 13.4. Bởi vì việc mở rộng các lớp C mục tiêu trong Swift được triển khai thông qua các thể loại C Mục tiêu. Các phương thức ghi đè thông qua các loại Mục tiêu C không được khuyến nghị và có khả năng phá vỡ. Xem stackoverflow.com/a/38274660/2438634
Marc Etcheverry

79

Đối với bất cứ ai vẫn đang vật lộn với điều này, phần mở rộng đơn giản này trong swift sẽ khắc phục vấn đề cho bạn.

extension UINavigationController {
    override open var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }
}

10
Bạn xứng đáng với một huy chương.
nikans

2
Cảm ơn rất nhiều chàng trai. Tôi đã quay trở lại thấyViewContaptor thay vì không thành công.
Fábio Salata

1
Đây là vàng. Tôi có một bộ điều khiển điều hướng được nhúng trong một thanh tab và tôi chỉ cần ném nó vào một tệp và bây giờ tôi có thể thay đổi giao diện thanh trạng thái bất cứ nơi nào tôi muốn.
Vahid Amiri

2
Điều này là sai và nó bị hỏng trong iOS 13.4. Bởi vì việc mở rộng các lớp C mục tiêu trong Swift được triển khai thông qua các thể loại C Mục tiêu. Các phương thức ghi đè thông qua các loại Mục tiêu C không được khuyến nghị và có khả năng phá vỡ. Xem stackoverflow.com/a/38274660/2438634
Marc Etcheverry

1
@MarcEtcheverry trường hợp cụ thể này không sai. Thực tế của vấn đề là các lớp con của các đối tượng / giao thức khác như UINavestionControll không có triển khai trước những điều này để xung đột trong công văn động. Không có mặc định hoặc triển khai trong các lớp con thực tế, đó là lý do tại sao đây là cách sạch nhất để thực hiện điều này trên một ứng dụng mà không tạo ra sự phụ thuộc không cần thiết (thời gian). Thật không may, 13.4 dường như đã thay đổi hành vi này. Tôi đoán đằng sau hậu trường họ có một kiểm tra hoặc triển khai hiện không tồn tại trong nhiều năm .........
TheCodingArt

20

Ứng dụng của tôi sử dụng cả ba: UINavigationController, UISplitViewController, UITabBarController, do đó những vẻ tất cả để giành quyền kiểm soát trên thanh trạng thái và sẽ gây ra preferedStatusBarStyleđể không bị gọi là cho con cái của họ. Để ghi đè hành vi này, bạn có thể tạo một phần mở rộng như phần còn lại của các câu trả lời đã đề cập. Đây là một phần mở rộng cho cả ba, trong Swift 4. Chúc Apple hiểu rõ hơn về loại công cụ này.

extension UINavigationController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return self.topViewController
    }

    open override var childViewControllerForStatusBarHidden: UIViewController? {
        return self.topViewController
    }
}

extension UITabBarController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return self.childViewControllers.first
    }

    open override var childViewControllerForStatusBarHidden: UIViewController? {
        return self.childViewControllers.first
    }
}

extension UISplitViewController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return self.childViewControllers.first
    }

    open override var childViewControllerForStatusBarHidden: UIViewController? {
        return self.childViewControllers.first
    }
}

Chỉnh sửa: Cập nhật các thay đổi API Swift 4.2

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }

    open override var childForStatusBarHidden: UIViewController? {
        return self.topViewController
    }
}

extension UITabBarController {
    open override var childForStatusBarStyle: UIViewController? {
        return self.children.first
    }

    open override var childForStatusBarHidden: UIViewController? {
        return self.children.first
    }
}

extension UISplitViewController {
    open override var childForStatusBarStyle: UIViewController? {
        return self.children.first
    }

    open override var childForStatusBarHidden: UIViewController? {
        return self.children.first
    }
}

1
Đây là giải pháp duy nhất hoạt động. Tất cả các câu trả lời trên SO đều chỉ ra giải pháp chuẩn sẽ không hoạt động đối với bất kỳ ứng dụng nào với NavigationControllers. Cảm ơn bạn!!!
Houman

Sử dụng phần mở rộng để ghi đè là sai. Điều đó không an toàn. Có nhiều giải pháp đơn giản hơn. Sử dụng một lớp con thay thế.
Sulthan

2
Điều này là sai và nó bị hỏng trong iOS 13.4. Bởi vì việc mở rộng các lớp C mục tiêu trong Swift được triển khai thông qua các thể loại C Mục tiêu. Các phương thức ghi đè thông qua các loại Mục tiêu C không được khuyến nghị và có khả năng phá vỡ. Xem stackoverflow.com/a/38274660/2438634
Marc Etcheverry

1
@MarcEtcheverry trường hợp cụ thể này không sai. Thực tế của vấn đề là các lớp con của các đối tượng / giao thức khác như UINavestionControll không có triển khai trước những điều này để xung đột trong công văn động. Không có mặc định hoặc triển khai trong các lớp con thực tế, đó là lý do tại sao đây là cách sạch nhất để thực hiện điều này trên một ứng dụng mà không tạo ra sự phụ thuộc không cần thiết (thời gian). Thật không may, 13.4 dường như đã thay đổi hành vi này. Tôi đoán đằng sau hậu trường họ có một kiểm tra hoặc triển khai hiện không tồn tại trong nhiều năm .........
TheCodingArt

15

Câu trả lời của Tyson là chính xác để thay đổi màu thanh trạng thái thành màu trắng UINavigationController.

Nếu bất cứ ai muốn thực hiện cùng một kết quả bằng cách viết mã vào AppDelegatethì sử dụng mã bên dưới và viết mã bên trongAppDelegate's didFinishLaunchingWithOptions phương thức.

Và đừng quên để thiết lập UIViewControllerBasedStatusBarAppearanceđểYES trong file plist, nếu không thay đổi sẽ không phản ánh.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     // status bar appearance code
     [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];

     return YES;
}

14

Trên một UINavestionControll, preferredStatusBarStylekhông được gọi vì nó topViewControllerđược ưa thích hơn self. Vì vậy, để được preferredStatusBarStylegọi trên UINavestionControll, bạn cần thay đổichildViewControllerForStatusBarStyle .

sự giới thiệu

Ghi đè Trình điều khiển UINavestion của bạn trong lớp của bạn:

class MyRootNavigationController: UINavigationController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}

Không đề xuất thay thế

Để làm điều đó cho tất cả UINavestionControll, bạn có thể ghi đè lên một tiện ích mở rộng (cảnh báo: nó ảnh hưởng đến UIDocumentPickerViewControll, UIImagePickerContoder, v.v.), nhưng có lẽ bạn không nên làm điều đó theo tài liệu Swift :

extension UINavigationController {
    open override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}

11

Ngoài câu trả lời của serenn, nếu bạn đang trình bày bộ điều khiển xem với modalPresentationStyle (ví dụ .overCurrentContext), bạn cũng nên gọi nó trên bộ điều khiển xem mới được trình bày:

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

Đừng quên ghi đè lên preferredStatusBarStyletrong trình điều khiển xem được trình bày.


9

Một bổ sung cho câu trả lời của Hippo: nếu bạn đang sử dụng Trình điều khiển UINavestion, thì có lẽ tốt hơn là thêm một danh mục:

//  UINavigationController+StatusBarStyle.h:

@interface UINavigationController (StatusBarStyle)

@end



//  UINavigationController+StatusBarStyle.m:

@implementation UINavigationController (StatusBarStyle)

- (UIStatusBarStyle)preferredStatusBarStyle
{
    //also you may add any fancy condition-based code here
    return UIStatusBarStyleLightContent;
}

@end

Giải pháp đó có lẽ tốt hơn là chuyển sang hành vi sắp bị phản đối.


Đừng làm điều này, nó hoạt động ngay bây giờ nhưng có thể phá vỡ hành vi trong tương lai. Chỉ cần thay đổi kiểu navBar - xem câu trả lời của tôi stackoverflow.com/a/19513714/505457
Tyson

2
Bạn nên sử dụng lớp con, không phải thể loại.
shuiyouren

2Tyson: Tại sao nó sẽ phá vỡ hành vi trong tương lai? preferStatusBarStyle: là phương thức ưa thích của Apple để thiết lập kiểu Thanh trạng thái.
Artem Abramov

2 Shuiyouren: Tại sao tôi nên tăng độ phức tạp bằng cách phân lớp nếu tôi chỉ có thể sử dụng một danh mục và đưa nó vào mọi nơi tôi sẽ đến? Dù sao, đó là một câu hỏi về kiến ​​trúc, không phải việc thực hiện.
Artem Abramov

2
@ArtemAbramov Bởi vì UINavestionControll đã triển khai preferredStatusBarStylevà thực hiện logic cụ thể của UINavestionControll . Ngay bây giờ logic này dựa trên navigationBar.barStylenhưng tôi có thể thấy các kiểm tra bổ sung được thêm vào (ví dụ: UISearchDisplayControllerdi chuyển để ẩn chế độ thanh điều hướng). Bằng cách ghi đè logic mặc định, bạn mất hết chức năng này và để mình mở cho những khoảnh khắc 'wtf' khó chịu trong tương lai. Xem câu trả lời của tôi ở trên để biết cách chính xác để làm điều này trong khi vẫn hỗ trợ hành vi điều khiển điều hướng được xây dựng.
Tyson

9

Swift 4.2 trở lên

Như đã đề cập trong câu trả lời được chọn , nguyên nhân gốc là để kiểm tra đối tượng điều khiển xem cửa sổ gốc của bạn.

Các trường hợp có thể có của cấu trúc dòng chảy của bạn

  • Đối tượng UIViewContaptor tùy chỉnh là bộ điều khiển xem cửa sổ gốc Bộ điều khiển xem cửa sổ gốc

    của bạn là một đối tượng UIViewControll và nó tiếp tục thêm hoặc xóa bộ điều khiển điều hướng hoặc tabContoder dựa trên luồng ứng dụng của bạn.

    Loại luồng này thường được sử dụng nếu ứng dụng của bạn có luồng đăng nhập trước trên ngăn xếp điều hướng mà không có tab và đăng luồng đăng nhập với các tab và có thể mọi tab tiếp tục giữ bộ điều khiển điều hướng.

  • Đối tượng TabBarContoder là trình điều khiển xem cửa sổ gốc

    Đây là luồng trong đó trình điều khiển xem gốc của cửa sổ là tabBarContoder có thể mỗi tab tiếp tục giữ bộ điều khiển điều hướng.

  • Đối tượng NavigationContoder là trình điều khiển xem cửa sổ gốc

    Đây là luồng trong đó trình điều khiển xem gốc của cửa sổ là navigationContoder.

    Tôi không chắc chắn nếu có bất kỳ khả năng để thêm bộ điều khiển thanh tab hoặc bộ điều khiển điều hướng mới trong bộ điều khiển điều hướng hiện có. Nhưng nếu có trường hợp như vậy, chúng ta cần chuyển điều khiển kiểu thanh trạng thái sang vùng chứa tiếp theo. Vì vậy, tôi đã thêm kiểm tra tương tự trong phần mở rộng UINavestionControll để tìmchildForStatusBarStyle

Sử dụng các tiện ích mở rộng sau, nó xử lý tất cả các tình huống trên -

extension UITabBarController {
    open override var childForStatusBarStyle: UIViewController? {
        return selectedViewController?.childForStatusBarStyle ?? selectedViewController
    }
}

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return topViewController?.childForStatusBarStyle ?? topViewController
    }
}

extension AppRootViewController {
    open override var preferredStatusBarStyle: UIStatusBarStyle {
        return children.first { $0.childForStatusBarStyle != nil }?.childForStatusBarStyle?.preferredStatusBarStyle ?? .default
    }
}
  • Theo mặc định, bạn không cần UIViewControllerBasedStatusBarAppearancekhóa info.plistđúng

Các điểm cần xem xét cho các luồng phức tạp hơn

  • Trong trường hợp bạn trình bày luồng mới theo phương thức, nó sẽ tách khỏi luồng kiểu thanh trạng thái hiện có. Vì vậy, giả sử bạn đang trình bày NewFlowUIViewControllervà sau đó thêm điều hướng mới hoặc bộ điều khiển tabBar vào NewFlowUIViewController, sau đó thêm phần mở rộng NewFlowUIViewControllercũng để quản lý kiểu thanh trạng thái của trình điều khiển xem thêm.

  • Trong trường hợp bạn đặt modalPftimeationStyle khác với fullScreenkhi trình bày phương thức, bạn phải đặt modalPresentationCapturesStatusBarAppearancethành đúng để trình điều khiển xem được trình bày phải nhận được điều khiển xuất hiện trên thanh trạng thái.


Câu trả lời tuyệt vời!
Amin Benarieb

3
Điều này là sai và nó bị hỏng trong iOS 13.4. Bởi vì việc mở rộng các lớp C mục tiêu trong Swift được triển khai thông qua các thể loại C Mục tiêu. Các phương thức ghi đè thông qua các loại Mục tiêu C không được khuyến nghị và có khả năng phá vỡ. Xem stackoverflow.com/a/38274660/2438634
Marc Etcheverry

@MarcEtcheverry trường hợp cụ thể này không sai. Thực tế của vấn đề là các lớp con của các đối tượng / giao thức khác như UINavestionControll không có triển khai trước những điều này để xung đột trong công văn động. Không có mặc định hoặc triển khai trong các lớp con thực tế, đó là lý do tại sao đây là cách sạch nhất để thực hiện điều này trên một ứng dụng mà không tạo ra sự phụ thuộc không cần thiết (thời gian). Thật không may, 13.4 dường như đã thay đổi hành vi này. Tôi đoán đằng sau hậu trường họ có một kiểm tra hoặc triển khai hiện không tồn tại trong nhiều năm .........
TheCodingArt

8

Giải pháp iOS 13

UINavigationControllerlà một lớp con của UIViewController(người biết)!

Do đó, khi trình bày bộ điều khiển chế độ xem được nhúng trong bộ điều khiển điều hướng, bạn không thực sự trình bày bộ điều khiển chế độ xem được nhúng; bạn đang trình bày các bộ điều khiển điều hướng! UINavigationController, như là một lớp con của UIViewController, kế thừa preferredStatusBarStylechildForStatusBarStyle , mà bạn có thể đặt như mong muốn.

Bất kỳ phương pháp nào sau đây cũng nên hoạt động:

  1. Chọn hoàn toàn khỏi Chế độ tối
    • Trong bạn info.plist , thêm thuộc tính sau:
      • Chìa khóa - UIUserInterfaceStyle (còn gọi là "Kiểu giao diện người dùng")
      • Giá trị - Ánh sáng
  2. Ghi đè preferredStatusBarStyletrongUINavigationController

    • preferredStatusBarStyle( doc ) - Kiểu thanh trạng thái ưa thích cho trình điều khiển xem
    • Phân lớp hoặc mở rộng UINavigationController

      class MyNavigationController: UINavigationController {
          override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }

      HOẶC LÀ

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  3. Ghi đè childForStatusBarStyletrongUINavigationController

    • childForStatusBarStyle( tài liệu ) - Được gọi khi hệ thống cần bộ điều khiển xem để sử dụng để xác định kiểu thanh trạng thái
    • Theo tài liệu của Apple,

      "Nếu trình điều khiển chế độ xem vùng chứa của bạn xuất phát kiểu thanh trạng thái từ một trong các trình điều khiển chế độ xem con của nó, [ghi đè thuộc tính này] và trả về trình điều khiển chế độ xem con đó. Nếu bạn trả về nil hoặc không ghi đè phương thức này, kiểu tự thanh trạng thái sẽ được sử dụng . Nếu giá trị trả về từ phương thức này thay đổi, hãy gọi phương thức setNeedStatusBarAppparentUpdate (). "

    • Nói cách khác, nếu bạn không thực hiện giải pháp 3 ở đây, hệ thống sẽ quay trở lại giải pháp 2 ở trên.
    • Phân lớp hoặc mở rộng UINavigationController

      class MyNavigationController: UINavigationController {
          override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }

      HOẶC LÀ

      extension UINavigationController {    
          open override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }
    • Bạn có thể trả về bất kỳ bộ điều khiển xem nào bạn muốn ở trên. Tôi đề nghị một trong những điều sau đây:

      • topViewController(of UINavigationController) ( doc ) - Bộ điều khiển xem ở đầu ngăn xếp điều hướng
      • visibleViewController(of UINavigationController) ( doc ) - Trình điều khiển chế độ xem được liên kết với chế độ xem hiện tại trong giao diện điều hướng (gợi ý: điều này có thể bao gồm "trình điều khiển chế độ xem được trình bày một cách vừa phải trên đầu bộ điều khiển điều hướng")

Lưu ý: Nếu bạn quyết định phân lớp UINavigationController, hãy nhớ áp dụng lớp đó cho bộ điều khiển điều hướng của bạn thông qua trình kiểm tra danh tính trong IB.

PS Mã của tôi sử dụng cú pháp Swift 5.1


Thanh trạng thái của tôi bị đen sau khi xoay màn hình. Bất cứ ý tưởng tại sao? Điều này chỉ xảy ra trên trình giả lập iPad Pro.
Pedro Paulo Amorim

@PedroPauloAmorim, bạn có thể cung cấp thêm thông tin không? Trình điều khiển xem hàng đầu được trình bày như thế nào (phương thức, toàn màn hình, hiển thị)? Có phải nó được lồng bên trong một bộ điều khiển điều hướng? Là văn bản chuyển sang màu đen, hoặc nền quá? Bạn đang cố gắng để thực hiện?
Andrew Kirna

Tôi đặt thanh trạng thái ánh sáng trong toàn bộ ứng dụng của mình. Nó nhận được ánh sáng trong hai lần quay, trong lần thứ ba, nó chuyển sang màu tối và không bao giờ trở lại ánh sáng, thậm chí buộc phải vẽ lại nó. Nó đang diễn ra trên trình giả lập iPad Pro. Các khung nhìn đang được trình bày ở chế độ toàn màn hình và chúng không được lồng trong bộ điều khiển điều hướng. Chỉ có văn bản chuyển sang màu tối.
Pedro Paulo Amorim

Làm thế nào để bạn thiết lập thanh trạng thái ánh sáng ở nơi đầu tiên?
Andrew Kirna

3
Ghi đè của bạn thông qua tiện ích mở rộng không phải là ghi đè thực sự. Đó là một sự lạm dụng không an toàn của ngôn ngữ. Điều đó có thể phá vỡ rất dễ dàng.
Sulthan

7

Câu trả lời của @ serenn ở trên vẫn là một câu trả lời tuyệt vời cho trường hợp của UINavestionControllers. Tuy nhiên, đối với swift 3, các hàm childViewControll đã được thay đổi thành vars. Vì vậy, UINavigationControllermã mở rộng phải là:

override open var childViewControllerForStatusBarStyle: UIViewController? {
  return topViewController
}

override open var childViewControllerForStatusBarHidden: UIViewController? {
  return topViewController
}

Và sau đó trong trình điều khiển chế độ xem nên ra lệnh kiểu thanh trạng thái:

override var preferredStatusBarStyle: UIStatusBarStyle {
   return .lightContent
}

2
Điều này là sai và nó bị hỏng trong iOS 13.4. Bởi vì việc mở rộng các lớp C mục tiêu trong Swift được triển khai thông qua các thể loại C Mục tiêu. Các phương thức ghi đè thông qua các loại Mục tiêu C không được khuyến nghị và có khả năng phá vỡ. Xem stackoverflow.com/a/38274660/2438634
Marc Etcheverry

@MarcEtcheverry trường hợp cụ thể này không sai. Thực tế của vấn đề là các lớp con của các đối tượng / giao thức khác như UINavestionControll không có triển khai trước những điều này để xung đột trong công văn động. Không có mặc định hoặc triển khai trong các lớp con thực tế, đó là lý do tại sao đây là cách sạch nhất để thực hiện điều này trên một ứng dụng mà không tạo ra sự phụ thuộc không cần thiết (thời gian). Thật không may, 13.4 dường như đã thay đổi hành vi này. Tôi đoán đằng sau hậu trường họ có một kiểm tra hoặc triển khai hiện không tồn tại trong nhiều năm .........
TheCodingArt


4

UIStatusBarStyle trong iOS 7

Thanh trạng thái trong iOS 7 là trong suốt, chế độ xem phía sau hiển thị thông qua.

Phong cách của thanh trạng thái đề cập đến sự xuất hiện của nội dung của nó. Trong iOS 7, nội dung trên thanh trạng thái là dark ( UIStatusBarStyleDefault) hoặc light ( UIStatusBarStyleLightContent). Cả hai UIStatusBarStyleBlackTranslucentUIStatusBarStyleBlackOpaquekhông được dùng trong iOS 7.0. Sử dụngUIStatusBarStyleLightContent thay thế.

Cách thay đổi UIStatusBarStyle

Nếu bên dưới thanh trạng thái là thanh điều hướng, kiểu thanh trạng thái sẽ được điều chỉnh để phù hợp với kiểu thanh điều hướng (UINavigationBar.barStyle ):

Cụ thể, nếu kiểu thanh điều hướng là UIBarStyleDefault, kiểu thanh trạng thái sẽ là UIStatusBarStyleDefault; nếu kiểu thanh điều hướng là UIBarStyleBlack, kiểu thanh trạng thái sẽ làUIStatusBarStyleLightContent .

Nếu không có thanh điều hướng bên dưới thanh trạng thái, kiểu thanh trạng thái có thể được điều khiển và thay đổi bởi một bộ điều khiển xem riêng lẻ trong khi ứng dụng chạy.

- [UIViewController preferredStatusBarStyle]là một phương thức mới được thêm vào trong iOS 7. Nó có thể được ghi đè để trả về kiểu thanh trạng thái ưa thích:

- (UIStatusBarStyle)preferredStatusBarStyle
  {
      return UIStatusBarStyleLightContent;
  }

Nếu kiểu thanh trạng thái nên được điều khiển bởi trình điều khiển xem con thay vì tự, ghi đè -[UIViewController childViewControllerForStatusBarStyle] để trả về bộ điều khiển xem con đó.

Nếu bạn muốn từ chối hành vi này và đặt kiểu thanh trạng thái bằng cách sử dụng -[UIApplication statusBarStyle]phương thức, hãy thêm UIViewControllerBasedStatusBarAppearancekhóa vào Info.plisttệp của ứng dụng và cung cấp cho nó giá trị KHÔNG.


3

Nếu bất cứ ai đang sử dụng Bộ điều khiển Điều hướng và muốn tất cả các bộ điều khiển điều hướng của họ có kiểu màu đen, bạn có thể viết một phần mở rộng cho UINavestionContoder như thế này trong Swift 3 và nó sẽ áp dụng cho tất cả các bộ điều khiển điều hướng (thay vì gán nó cho một bộ điều khiển tại thời gian).

extension UINavigationController {

    override open func viewDidLoad() {
        super.viewDidLoad()

        self.navigationBar.barStyle = UIBarStyle.black
    }

}

1
Nhưng nếu thanh điều hướng của tôi bị ẩn thì sao?
Slavcho

1
Bởi vì tôi cần điều hướng để được ẩn và thanh trạng thái được hiển thị.
Slavcho

1

Trong Swift cho bất kỳ loại UIViewControll nào:

Trong AppDelegatebộ của bạn :

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    window!.rootViewController = myRootController
    return true
}

myRootControllercó thể là bất kỳ loại nào UIViewController, ví dụ UITabBarControllerhoặc UINavigationController.

Sau đó, ghi đè bộ điều khiển gốc như thế này:

class RootController: UIViewController {
    override func preferredStatusBarStyle() -> UIStatusBarStyle {
        return .LightContent
    }
}

Điều này sẽ thay đổi giao diện của thanh trạng thái trong toàn bộ ứng dụng của bạn, bởi vì bộ điều khiển gốc chỉ chịu trách nhiệm cho sự xuất hiện của thanh trạng thái.

Hãy nhớ đặt thuộc tính View controller-based status bar appearancethành CÓ trong của bạn Info.plistđể làm cho công việc này (là mặc định).


@Làm thế nào trong swift3?
máy bay

1

Giải pháp Swift 3 iOS 10:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
 }

1

Hầu hết các câu trả lời không bao gồm triển khai tốt childViewControllerForStatusBarStylephương pháp cho UINavigationController. Theo kinh nghiệm của tôi, bạn nên xử lý các trường hợp như khi trình điều khiển xem trong suốt được trình bày trên bộ điều khiển điều hướng. Trong những trường hợp này, bạn nên chuyển điều khiển cho bộ điều khiển phương thức ( visibleViewController), nhưng không phải khi nó biến mất.

override var childViewControllerForStatusBarStyle: UIViewController? {
  var childViewController = visibleViewController
  if let controller = childViewController, controller.isBeingDismissed {
    childViewController = topViewController
  }
  return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}

1

Trong trường hợp của tôi, tôi đã vô tình trình bày Bộ điều khiển Chế độ xem / Điều hướng UIModalPresentationStyle.overFullScreen, nguyên nhân preferredStatusBarStylekhông được gọi. Sau khi chuyển nó trở lại UIModalPresentationStyle.fullScreen, mọi thứ hoạt động.


1

Đối với iOS 13.4, preferredStatusBarStylephương thức trong UINavigationControllerdanh mục sẽ không được gọi, swizzling dường như là tùy chọn duy nhất mà không cần sử dụng lớp con.

Thí dụ:

Tiêu đề danh mục:

@interface UINavigationController (StatusBarStyle)
+ (void)setUseLightStatusBarStyle;
@end

Thực hiện:

#import "UINavigationController+StatusBarStyle.h"
#import <objc/runtime.h>

@implementation UINavigationController (StatusBarStyle)

void (^swizzle)(Class, SEL, SEL) = ^(Class c, SEL orig, SEL new){
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, new);
    if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
        class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    else
        method_exchangeImplementations(origMethod, newMethod);
};

+ (void)setUseLightStatusBarStyle {
    swizzle(self.class, @selector(preferredStatusBarStyle), @selector(_light_preferredStatusBarStyle));
}

- (UIStatusBarStyle)_light_preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}    
@end

Sử dụng trong AppDelegate.h:

#import "UINavigationController+StatusBarStyle.h"

[UINavigationController setUseLightStatusBarStyle];

0

Đây là phương pháp của tôi để giải quyết điều này.

Xác định một giao thức được gọi là AGViewControllAppparent .

AGViewControllAppurdy.h

#import <Foundation/Foundation.h>

@protocol AGViewControllerAppearance <NSObject>

@optional

- (BOOL)showsStatusBar;
- (BOOL)animatesStatusBarVisibility;
- (UIStatusBarStyle)preferredStatusBarStyle;
- (UIStatusBarAnimation)prefferedStatusBarAnimation;

@end

Xác định một danh mục trên UIViewControll gọi là Nâng cấp .

UIViewControll + Nâng cấp.h

#import <UIKit/UIKit.h>

@interface UIViewController (Upgrade)

//
//  Replacements
//

- (void)upgradedViewWillAppear:(BOOL)animated;

@end

UIViewControll + Nâng cấp.m

#import "UIViewController+Upgrade.h"

#import <objc/runtime.h>

#import "AGViewControllerAppearance.h" // This is the appearance protocol

@implementation UIViewController (Upgrade)

+ (void)load
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wselector"
    Method viewWillAppear = class_getInstanceMethod(self, @selector(viewWillAppear:));
#pragma clang diagnostic pop
    Method upgradedViewWillAppear = class_getInstanceMethod(self, @selector(upgradedViewWillAppear:));
    method_exchangeImplementations(viewWillAppear, upgradedViewWillAppear);
}

#pragma mark - Implementation

- (void)upgradedViewWillAppear:(BOOL)animated
{
    //
    //  Call the original message (it may be a little confusing that we're
    //  calling the 'same' method, but we're actually calling the original one :) )
    //

    [self upgradedViewWillAppear:animated];

    //
    //  Implementation
    //

    if ([self conformsToProtocol:@protocol(AGViewControllerAppearance)])
    {
        UIViewController <AGViewControllerAppearance> *viewControllerConformingToAppearance =
        (UIViewController <AGViewControllerAppearance> *)self;

        //
        //  Status bar
        //

        if ([viewControllerConformingToAppearance respondsToSelector:@selector(preferredStatusBarStyle)])
        {
            BOOL shouldAnimate = YES;

            if ([viewControllerConformingToAppearance respondsToSelector:@selector(animatesStatusBarVisibility)])
            {
                shouldAnimate = [viewControllerConformingToAppearance animatesStatusBarVisibility];
            }

            [[UIApplication sharedApplication] setStatusBarStyle:[viewControllerConformingToAppearance preferredStatusBarStyle]
                                                        animated:shouldAnimate];
        }

        if ([viewControllerConformingToAppearance respondsToSelector:@selector(showsStatusBar)])
        {
            UIStatusBarAnimation animation = UIStatusBarAnimationSlide;

            if ([viewControllerConformingToAppearance respondsToSelector:@selector(prefferedStatusBarAnimation)])
            {
                animation = [viewControllerConformingToAppearance prefferedStatusBarAnimation];
            }

            [[UIApplication sharedApplication] setStatusBarHidden:(! [viewControllerConformingToAppearance showsStatusBar])
                                                    withAnimation:animation];
        }
    }
}

@end

Bây giờ, đã đến lúc nói rằng bộ điều khiển xem của bạn đang triển khai AGViewControllAppurdy giao thức .

Thí dụ:

@interface XYSampleViewController () <AGViewControllerAppearance>

... the rest of the interface

@end

Tất nhiên, bạn có thể triển khai phần còn lại của các phương thức ( showsStatusBar , animatesStatusBarVisibility , prefferedStatusBarAnimation ) từ giao thức và UIViewControll + Nâng cấp sẽ thực hiện tùy chỉnh phù hợp dựa trên các giá trị được cung cấp bởi chúng.


0

Nếu ai đó gặp phải vấn đề này với UISearchControll. Chỉ cần tạo một lớp con mới của UISearchControll và sau đó thêm mã bên dưới vào lớp đó:

override func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}

0

Lưu ý rằng khi sử dụng self.navigationController.navigationBar.barStyle = UIBarStyleBlack; giải pháp

hãy chắc chắn đi đến phần chính của bạn và đặt "Xem giao diện thanh trạng thái dựa trên bộ điều khiển" thành CÓ. Nếu nó KHÔNG nó sẽ không hoạt động.


Việc đặt UIViewControllBasingStatusBarAppurdy thành CÓ trong phần dự án đã tạo ra tất cả sự khác biệt cho tôi. Tôi đã quên nó rồi.
filo

0

Kể từ Xcode 11.4, ghi đè preferredStatusBarStyle tính trong tiện ích mở rộng UINavestionControll không còn hoạt động nữa vì nó sẽ không được gọi.

Thiết lập barStylecủa navigationBarđể .blackcông trình thực sự nhưng điều này sẽ bổ sung thêm các tác dụng phụ không mong muốn nếu bạn thêm subviews đến Navigationbar có thể có bệnh cảnh khác nhau cho chế độ ánh sáng và bóng tối. Bởi vì bằng cách đặt barStylethành màu đen, userInterfaceStylechế độ xem được nhúng trong navigationBar sau đó sẽ luôn có userInterfaceStyle.darkbất kểuserInterfaceStyle ứng dụng nào.

Giải pháp thích hợp tôi đưa ra là bằng cách thêm một lớp con UINavigationControllervà ghi đè lên preferredStatusBarStyleđó. Nếu sau đó bạn sử dụng UINavestionControll tùy chỉnh này cho tất cả các chế độ xem của bạn, bạn sẽ ở bên lưu.


-1

NavigationContoder hoặc TabBarControll là những cái cần cung cấp kiểu. Đây là cách tôi giải quyết: https://stackoverflow.com/a/39072526/242769


Nếu bạn nghĩ rằng đây là một bản sao của một câu hỏi khác, vui lòng đóng bình chọn là trùng lặp
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.