Có thể từ chối chế độ tối trên iOS 13 không?


295

Một phần lớn trong ứng dụng của tôi bao gồm các lượt xem web để cung cấp chức năng chưa có sẵn thông qua các triển khai gốc. Nhóm web không có kế hoạch để thực hiện một chủ đề tối cho trang web. Như vậy, ứng dụng của tôi sẽ trông hơi nửa / nửa với hỗ trợ Chế độ tối trên iOS 13.

Có thể từ chối hỗ trợ Chế độ tối để ứng dụng của chúng tôi luôn hiển thị chế độ sáng để phù hợp với chủ đề trang web không?


70
Đặt UIUserInterfaceStylethành Lighttrong Info.Plist của bạn. Xem developer.apple.com/l
Library / archive / document / General / từ

1
Cảm ơn đã hỏi - cho tất cả chúng ta. Rất nhiều ứng dụng để đi qua. Điều này là cần thiết để giữ cho các ứng dụng hoạt động cho đến khi việc chuyển đổi đã sẵn sàng.
dùng3741598

nhập nền tảng Nhập khẩu tiện ích mở rộng UIKit UIViewControll {ghi đè mở func awakeFromNib () {super.awakeFromNib () nếu #av Available (iOS 13.0, *) {// Luôn áp dụng kiểu giao diện nhẹ. overrideUserInterfaceStyle = .light}}}
Mohammad Razipour

1
chỉ cần thêm UIUserInterfaceStyle trong phần chính. thật dễ dàng
Fattie

Trong khi gửi ứng dụng tới cửa hàng ứng dụng, apple chấp nhận do UIUserInterfaceStyle ở chế độ Light.
kiran

Câu trả lời:


683

Đầu tiên, đây là mục nhập của Apple liên quan đến việc từ chối chế độ tối. Nội dung tại liên kết này được viết cho Xcode 11 & iOS 13 :

Phần này áp dụng cho việc sử dụng Xcode 11


Nếu bạn muốn từ chối ứng dụng ENTIRE của bạn

Cách tiếp cận số 1

Sử dụng khóa sau trong tệp info.plist của bạn :

UIUserInterfaceStyle

Và gán cho nó một giá trị Light.

Các XML cho UIUserInterfaceStylecông việc:

<key>UIUserInterfaceStyle</key>
<string>Light</string>

Cách tiếp cận số 2

Bạn có thể thiết lập overrideUserInterfaceStyledựa vào windowbiến của ứng dụng .

Tùy thuộc vào cách dự án của bạn được tạo, điều này có thể nằm trong AppDelegatetệp hoặc SceneDelegate.

if #available(iOS 13.0, *) {
    window?.overrideUserInterfaceStyle = .light
}


Nếu bạn muốn từ chối UIViewControll của mình trên cơ sở cá nhân

override func viewDidLoad() {
    super.viewDidLoad()
    // overrideUserInterfaceStyle is available with iOS 13
    if #available(iOS 13.0, *) {
        // Always adopt a light interface style.
        overrideUserInterfaceStyle = .light
    }
}

Tài liệu của Apple về ghi đèUserInterfaceStyle

Mã ở trên sẽ trông như thế nào trong Xcode 11:

nhập mô tả hình ảnh ở đây

Phần này áp dụng cho việc sử dụng Xcode 10.x


Nếu bạn đang sử dụng Xcode 11 để gửi, bạn có thể bỏ qua mọi thứ bên dưới dòng này một cách an toàn.

Vì API có liên quan không tồn tại trong iOS 12, bạn sẽ gặp lỗi khi cố gắng sử dụng các giá trị được cung cấp ở trên:

Để thiết lập overrideUserInterfaceStyletrong của bạnUIViewController

nhập mô tả hình ảnh ở đây

Nếu bạn muốn từ chối UIViewControll của mình trên cơ sở cá nhân

Điều này có thể được xử lý trong Xcode 10 bằng cách kiểm tra phiên bản trình biên dịch và phiên bản iOS:

#if compiler(>=5.1)
if #available(iOS 13.0, *) {
    // Always adopt a light interface style.
    overrideUserInterfaceStyle = .light
}
#endif

Nếu bạn muốn từ chối ứng dụng ENTIRE của bạn

Bạn có thể sửa đổi đoạn mã trên để hoạt động đối với toàn bộ ứng dụng cho Xcode 10, bằng cách thêm mã sau vào AppDelegatetệp của bạn .

#if compiler(>=5.1)
if #available(iOS 13.0, *) {
    // Always adopt a light interface style.
    window?.overrideUserInterfaceStyle = .light
}
#endif

Tuy nhiên, cài đặt plist sẽ thất bại khi sử dụng Xcode phiên bản 10.x:

nhập mô tả hình ảnh ở đây

Tín dụng cho @Aron Nelson , @Raimundas Sakalauskas , @NSLeaderrmaddy để cải thiện câu trả lời này với phản hồi của họ.


2
Ánh sáng UIUserInterfaceStyle bị chặn khi cập nhật / tải lên ứng dụng của bạn ngay bây giờ. Nó được gắn cờ là một mục nhập không hợp lệ. (Khóa plist không hợp lệ)
Aron Nelson

2
Điều này sẽ không được biên dịch dựa trên iOS SDK 12 (SDK ổn định mới nhất hiện tại). Xem stackoverflow.com/a/57521901/2249485 để biết giải pháp cũng sẽ hoạt động với SDK iOS 12.
Raimundas Sakalauskas

Điều này thật không công bằng khi câu hỏi có nhiều lượt xem hơn "câu hỏi gốc" bị khóa để cung cấp câu trả lời. :(
Raimundas Sakalauskas

7
Thay vì cài đặt overrideUserInterfaceStyletrong viewDidLoadmọi trình điều khiển chế độ xem, bạn có thể đặt nó một lần trên cửa sổ chính của ứng dụng. Vì vậy, dễ dàng hơn nhiều nếu bạn muốn toàn bộ ứng dụng hành xử theo một cách.
rmaddy

2
Sử dụng #if compiler(>=5.1)thay thế responds(to:)setValue
NSLeader

162

Theo phiên của Apple về "Thực hiện chế độ tối trên iOS" ( https://developer.apple.com/ideo/play/wwdc2019/214/ bắt đầu từ 31:13), có thể đặt overrideUserInterfaceStylethành UIUserInterfaceStyleLighthoặc UIUserInterfaceStyleDarktrên bất kỳ trình điều khiển xem hoặc xem nào , cái này sẽ được sử dụng trong traitCollectionbất kỳ bộ điều khiển phụ hoặc xem nào.

Như SeanR đã đề cập, bạn có thể đặt UIUserInterfaceStylethành LighthoặcDark trong file plist của ứng dụng của bạn để thay đổi điều này cho toàn bộ ứng dụng của bạn.


17
Nếu bạn đặt khóa UIUserInterfaceStyle, ứng dụng của bạn sẽ bị từ chối trong Cửa hàng ứng dụng
Sonius

2
Apple từ chối với itms-90.190 mã lỗi forums.developer.apple.com/thread/121028
PRASAD1240

11
Việc từ chối rất có thể xảy ra vì SDK iOS 13 chưa hết beta. Tôi nghĩ rằng điều này sẽ hoạt động ngay khi Xcode 11 GM có sẵn.
dorbeetle

2
@dorbeetle không đúng, tôi đã tải lên ứng dụng của mình bằng khóa này thành công như 1 tháng trước với Xcode 10. Sự từ chối xảy ra gần đây. Có vẻ như một số loại chiến lược mới của Apple.
steven

1
Nó vẫn đang diễn ra. Xcode GM2 trả về lỗi ký ứng dụng. Xcode 10.3 được trả về: "Khóa Info.plist không hợp lệ. Khóa 'UIUserInterfaceStyle' trong tệp Payload / Galileo.appInfo.plist không hợp lệ."
Evgen Bodunov

64

Nếu bạn không sử dụng Xcode 11 trở lên (i, e iOS 13 trở lên SDK), ứng dụng của bạn đã không tự động chọn để hỗ trợ chế độ tối. Vì vậy, không cần phải từ chối chế độ tối.

Nếu bạn đang sử dụng Xcode 11 trở lên, hệ thống đã tự động bật chế độ tối cho ứng dụng của bạn. Có hai cách tiếp cận để tắt chế độ tối tùy theo sở thích của bạn. Bạn có thể vô hiệu hóa hoàn toàn hoặc vô hiệu hóa nó cho bất kỳ cửa sổ, chế độ xem hoặc bộ điều khiển xem cụ thể nào.

Vô hiệu hóa chế độ tối hoàn toàn cho ứng dụng của bạn

Bạn có thể tắt chế độ tối bằng cách bao gồm UIUserInterfaceStylekhóa có giá trị như Lighttrong tệp Info.plist của ứng dụng. Điều này bỏ qua sở thích của người dùng và luôn áp dụng giao diện nhẹ cho ứng dụng của bạn.
UIUserInterfaceStyle là ánh sáng

Tắt chế độ tối cho Window, View hoặc View Controller

Bạn có thể buộc giao diện của mình luôn xuất hiện theo kiểu sáng hoặc tối bằng cách đặt thuộc overrideUserInterfaceStyletính của cửa sổ, chế độ xem hoặc bộ điều khiển xem thích hợp.

Xem bộ điều khiển:

override func viewDidLoad() {
    super.viewDidLoad()
    /* view controller’s views and child view controllers 
     always adopt a light interface style. */
    overrideUserInterfaceStyle = .light
}

Lượt xem:

// The view and all of its subviews always adopt light style.
youView.overrideUserInterfaceStyle = .light

Cửa sổ:

/* Everything in the window adopts the style, 
 including the root view controller and all presentation controllers that 
 display content in that window.*/
window.overrideUserInterfaceStyle = .light

Lưu ý: Apple rất khuyến khích hỗ trợ chế độ tối trong ứng dụng của bạn. Vì vậy, bạn chỉ có thể tắt chế độ tối tạm thời.

Đọc thêm tại đây: Chọn kiểu giao diện cụ thể cho ứng dụng iOS của bạn


34

********** Cách dễ nhất cho Xcode 11 trở lên ****** / 4

Thêm phần này vào info.plist trước </dict></plist>

<key>UIUserInterfaceStyle</key>
<string>Light</string>

giải pháp này sẽ thất bại khi gửi ứng dụng trên Xcode 10.x
Tawfik Bouabid

27

Tôi nghĩ rằng tôi đã tìm thấy giải pháp. Ban đầu tôi đã ghép nó từ UIUserInterfaceStyle - Danh sách tài sản thông tinUIUserInterfaceStyle - UIKit , nhưng giờ đây đã tìm thấy nó thực sự được ghi lại trong việc chọn kiểu giao diện cụ thể cho ứng dụng iOS của bạn .

Trong của bạn info.plist, đặt UIUserInterfaceStyle( Kiểu giao diện người dùng ) thành 1 ( UIUserInterfaceStyle.light).

EDIT: Theo câu trả lời của dorbeetle, UIUserInterfaceStylecó thể có một cài đặt phù hợp hơn Light.


Việc thực thi chế độ tối bằng cách đặt giá trị thành 2 không hoạt động:[UIInterfaceStyle] '2' is not a recognized value for UIUserInterfaceStyle. Defaulting to Light.
funkenstrahlen

3
Có khóa này trong danh sách sẽ dẫn đến sự từ chối của App Store.
Jose

1
AppStore không còn từ chối tài sản này trong plist.info. Tôi đặt "Dark" (viết hoa) vì ứng dụng của chúng tôi đã tối. Không vấn đề gì. Điều này đúng cho phép chúng tôi sử dụng các điều khiển hệ thống.
nickdnk

@nickdnk Tôi nghĩ bạn đã xây dựng ứng dụng của mình bằng Xcode 11, được Apple khuyên dùng.
DawnSong

1
Vâng, tôi đã làm. Nó không thay đổi thực tế rằng Apple chấp nhận tham số này trong phần chính, đó là điều tôi đang cố gắng làm rõ.
nickdnk

23

Câu trả lời ở trên hoạt động nếu bạn muốn từ chối toàn bộ ứng dụng. Nếu bạn đang làm việc trên lib có UI và bạn không cần chỉnh sửa .plist, bạn cũng có thể thực hiện thông qua mã.

Nếu bạn đang biên dịch dựa trên SDK iOS 13, bạn chỉ cần sử dụng mã sau đây:

Nhanh:

if #available(iOS 13.0, *) {
    self.overrideUserInterfaceStyle = .light
}

Obj-C:

if (@available(iOS 13.0, *)) {
    self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}

TUY NHIÊN , nếu bạn muốn mã của mình biên dịch theo SDK iOS 12 (hiện tại vẫn là SDK ổn định mới nhất), bạn nên sử dụng các công cụ chọn. Mã với bộ chọn:

Swift (XCode sẽ hiển thị các cảnh báo cho mã này, nhưng đó là cách duy nhất để làm điều đó vì tài sản không tồn tại trong SDK 12 do đó sẽ không được biên dịch):

if #available(iOS 13.0, *) {
    if self.responds(to: Selector("overrideUserInterfaceStyle")) {
        self.setValue(UIUserInterfaceStyle.light.rawValue, forKey: "overrideUserInterfaceStyle")
    }
}

Obj-C:

if (@available(iOS 13.0, *)) {
    if ([self respondsToSelector:NSSelectorFromString(@"overrideUserInterfaceStyle")]) {
        [self setValue:@(UIUserInterfaceStyleLight) forKey:@"overrideUserInterfaceStyle"];
    }
}

Sẽ tốt hơn nếu bạn chỉ định tài sản overrideUserInterfaceStylethuộc về cái gì .
DawnSong

12

Cập nhật mới nhất-

Nếu bạn đang sử dụng Xcode 10.x, thì mặc định UIUserInterfaceStylelightdành cho iOS 13.x. Khi chạy trên thiết bị iOS 13, nó sẽ chỉ hoạt động ở Chế độ ánh sáng.

Không cần thêm UIUserInterfaceStylekhóa một cách rõ ràng vào tệp Info.plist, thêm nó sẽ báo lỗi khi bạn Xác thực ứng dụng của mình, cho biết:

Khóa thông tin không hợp lệ. Khóa 'UIUserInterfaceStyle' trong tệp Payload / AppName.appInfo.plist không hợp lệ.

Chỉ thêm UIUserInterfaceStylekhóa trong tệp Info.plist khi sử dụng Xcode 11.x.


1
Điều này không liên quan gì đến Xcode 10 hoặc 11. Nếu người dùng triển khai mẫu ứng dụng Xcode 10 và không quan tâm đến chế độ tối, ứng dụng khi được cài đặt trong iPhone 11, Pro hoặc Pro Max sẽ có vấn đề về chế độ tối. bạn cần cập nhật lên Xcode 11 và giải quyết vấn đề này.
Niranjan Molkeri

3
@NiranjanMolkeri Điều này không liên quan gì đến những chiếc iPhone mới hơn. Đó là về chế độ Dark trên iOS 13. Trong các ứng dụng phiên bản iOS 13 beta trước đó, UI có vấn đề về chế độ tối nếu không được xử lý rõ ràng. Nhưng trong phiên bản mới nhất, điều đó đã được sửa. Nếu bạn đang sử dụng XCode 10, thì UIUserInterfaceStyle mặc định sẽ sáng cho iOS13. Nếu bạn đang sử dụng Xode11, bạn cần xử lý nó.
kumarsiddharth123

Bạn sẽ gặp vấn đề nếu bạn tải ứng dụng lên TestFligth bằng Xcode 10.3 và phần chính bao gồm UIUserInterfaceStyle chính. Nó sẽ nói rằng nó là một tập tin plist không hợp lệ. Bạn phải xóa nó nếu xây dựng trong Xcode 10 hoặc tải lên bằng Xcode 11
eharo2

9

Nếu bạn sẽ thêm UIUserInterfaceStylekhóa vào tệp plist, có thể Apple sẽ từ chối bản phát hành như được đề cập ở đây: https://stackoverflow.com/a/56546554/7524146 Dù sao, thật khó chịu khi nói rõ ràng với từng ViewContoder self.overrideUserInterfaceStyle = .light . Nhưng bạn có thể sử dụng mã hòa bình này một lần cho windowđối tượng gốc của mình :

if #available(iOS 13.0, *) {
    if window.responds(to: Selector(("overrideUserInterfaceStyle"))) {
        window.setValue(UIUserInterfaceStyle.light.rawValue, forKey: "overrideUserInterfaceStyle")
    }
}

Chỉ cần lưu ý rằng bạn không thể làm điều này bên trong application(application: didFinishLaunchingWithOptions:)bởi vì bộ chọn này sẽ không phản hồi trueở giai đoạn đầu đó. Nhưng bạn có thể làm điều đó sau. Thật dễ dàng nếu bạn đang sử dụng tùy chỉnh AppPresenterhoặc AppRouterlớp trong ứng dụng của mình thay vì bắt đầu UI trong AppDelegate tự động.


9

Bạn có thể tắt Chế độ tối trong toàn bộ ứng dụng trong Xcode 11:

  1. Đi Info.plist
  2. Thêm dưới đây như

    <key>UIUserInterfaceStyle</key>
    <string>Light</string>

Info.plist sẽ giống như bên dưới ...

nhập mô tả hình ảnh ở đây


1
không hoạt động cho Xcode Phiên bản 11.3.1 (11C504) vì một số lý do
Andrew

7

- Đối với toàn bộ Ứng dụng (Cửa sổ):

window!.overrideUserInterfaceStyle = .light

Bạn có thể lấy cửa sổ từ SceneDelegate

- Đối với một ViewContoder duy nhất:

viewController.overrideUserInterfaceStyle = .light

Bạn có thể đặt bất kỳ viewController, thậm chí bên trong viewController nó tự

- Đối với một Chế độ xem duy nhất:

view.overrideUserInterfaceStyle = .light

Bạn có thể đặt bất kỳ view, ngay cả trong chế độ xem nó tự

Bạn có thể cần sử dụng if #available(iOS 13.0, *) { ,,, }nếu bạn đang hỗ trợ các phiên bản iOS cũ hơn.


6

Ngoài các phản hồi khác, theo hiểu biết của tôi về những điều sau đây, bạn chỉ cần chuẩn bị cho chế độ Dark khi biên dịch với SDK iOS 13 (sử dụng XCode 11).

Hệ thống giả định rằng các ứng dụng được liên kết với SDK iOS 13 trở lên hỗ trợ cả ngoại hình sáng và tối. Trong iOS, bạn chỉ định giao diện cụ thể mà bạn muốn bằng cách chỉ định kiểu giao diện cụ thể cho cửa sổ, chế độ xem hoặc bộ điều khiển xem của bạn. Bạn cũng có thể tắt hoàn toàn hỗ trợ cho Chế độ tối bằng cách sử dụng khóa Info.plist.

Liên kết


2

Có, bạn có thể bỏ qua bằng cách thêm mã sau vào viewDidLoad:

if #available(iOS 13.0, *) {
        // Always adopt a light interface style.
        overrideUserInterfaceStyle = .light
    }

2

Hiện tại, ứng dụng của tôi không hỗ trợ chế độ tối và sử dụng màu thanh ứng dụng sáng. Tôi đã có thể buộc nội dung trên thanh trạng thái thành văn bản và biểu tượng tối bằng cách thêm khóa sau vào Info.plist:

<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDarkContent</string>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>

Tìm các giá trị khác có thể có tại đây: https://developer.apple.com/documentation/uikit/uistatusbarstyle


2

Phiên bản khách quan

 if (@available(iOS 13.0, *)) {
        _window.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
    }

1

Dưới đây là một số mẹo và thủ thuật mà bạn có thể sử dụng trong ứng dụng của mình để hỗ trợ hoặc bỏ qua chế độ tối.

Mẹo đầu tiên: Để ghi đè kiểu ViewContoder

bạn có thể ghi đè kiểu giao diện của UIViewControll bằng cách

1: overrideUserInterfaceStyle = .dark // Dành cho chế độ tối

2: overrideUserInterfaceStyle = .light // Dành cho chế độ ánh sáng

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        overrideUserInterfaceStyle = .light    
    }
}

Mẹo thứ hai: Thêm khóa trong info.plist

Đơn giản là bạn có thể thêm một khóa mới

UIUserInterfaceStyle

trong thông tin ứng dụng của bạn.plist và đặt giá trị của nó thành Sáng hoặc Tối. điều này sẽ ghi đè kiểu mặc định của ứng dụng vào giá trị bạn cung cấp. Bạn không phải thêm overrideUserInterfaceStyle = .light dòng này trong mỗi viewContoder, chỉ một dòng trong info.plist đó là nó.


1

Chỉ cần thêm khóa sau vào info.plisttệp của bạn :

<key>UIUserInterfaceStyle</key>
    <string>Light</string>

1
 if #available(iOS 13.0, *) {
            overrideUserInterfaceStyle = .light
        } else {
            // Fallback on earlier versions
        }

Bạn có thể giải thích một chút về cách trả lời này sẽ giải quyết vấn đề, thay vì đăng câu trả lời chỉ có mã.
Arun Vinoth

Vâng chắc chắn @ArunVinoth Trong iOS 13, chế độ tối được giới thiệu vì vậy nếu mục tiêu triển khai của bạn thấp hơn 13, hãy sử dụng mã ở trên, bạn có thể sử dụng câu lệnh đơn giản được viết trong khối if.
Talha Rasool

1

Swift 5

Hai cách để chuyển chế độ tối sang chế độ sáng:

1- thông tin

    <key>UIUserInterfaceStyle</key>
    <string>Light</string>

2- Lập trình

 UIApplication.shared.windows.forEach { window in
     window.overrideUserInterfaceStyle = .light
  } 

0

Tôi sẽ sử dụng giải pháp này vì thuộc tính cửa sổ có thể được thay đổi trong vòng đời ứng dụng. Vì vậy, việc gán "overrideUserInterfaceStyle = .light" cần được lặp lại. UIWindow.appparent () cho phép chúng ta đặt giá trị mặc định sẽ được sử dụng cho các đối tượng UIWindow mới được tạo.

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

      if #available(iOS 13.0, *) {
          UIWindow.appearance().overrideUserInterfaceStyle = .light
      }

      return true
    }
}

0

Chỉ cần thêm các dòng này trong tập tin info.plist:

<key>UIUserInterfaceStyle</key>
<string>light</string>

Điều này sẽ buộc ứng dụng chỉ chạy trong chế độ ánh sáng.


Điều này đã được bình luận và trả lời nhiều lần. Ngay cả câu trả lời được chấp nhận cũng gợi ý điều này. Do đó bình luận này không thêm bất kỳ thông tin mới.
JeroenJK

0
import UIKit

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}

2
Vui lòng thêm một số lời giải thích cho câu trả lời của bạn bằng cách chỉnh sửa nó, để những người khác có thể học hỏi từ nó
Nico Haase

0

Bạn có thể làm: thêm khóa UIUserInterfaceStyle mới này vào Info.plist và đặt giá trị của nó thành Light. và kiểm tra bộ điều khiển cảnh báo xuất hiện với chế độ ánh sáng.

UIUserInterfaceStyle Light Nếu bạn buộc chế độ sáng / tối trong toàn bộ ứng dụng của mình bất kể cài đặt của người dùng bằng cách thêm khóa UIUserInterfaceStyle vào tệp Info.plist của bạn và đặt giá trị của nó thành Sáng hoặc Tối.


0

Câu hỏi này có rất nhiều câu trả lời, thay vì sử dụng nó trong info.plistbạn có thể đặt nó AppDelegatenhư thế này:

#if compiler(>=5.1)
        if #available(iOS 13.0, *) {
            self.window?.overrideUserInterfaceStyle = .light
        }
        #endif

Thử nghiệm trên Xcode 11.3, iOS 13.3


-8

Trên thực tế tôi chỉ viết một số mã sẽ cho phép bạn từ chối toàn cầu chế độ tối trong mã mà không cần phải đặt với mọi bộ điều khiển viw trong ứng dụng của bạn. Điều này có lẽ có thể được tinh chỉnh để từ chối một lớp theo cơ sở lớp bằng cách quản lý một danh sách các lớp. Đối với tôi, những gì tôi muốn là cho người dùng của tôi xem họ có thích giao diện chế độ tối cho ứng dụng của tôi không và nếu họ không thích nó, họ có thể tắt nó đi. Điều này sẽ cho phép họ tiếp tục sử dụng chế độ tối cho phần còn lại của ứng dụng.

Lựa chọn của người dùng là tốt (Ahem, nhìn vào Apple của bạn, đây là cách bạn nên thực hiện nó).

Vì vậy, cách thức hoạt động của nó là nó chỉ là một thể loại của UIViewControll. Khi tải, nó thay thế phương thức viewDidLoad riêng bằng một phương thức sẽ kiểm tra cờ toàn cầu để xem chế độ tối có bị vô hiệu hóa cho mọi thứ hay không.

Vì được kích hoạt khi tải UIViewControll, nó sẽ tự động khởi động và tắt chế độ tối theo mặc định. Nếu đây không phải là điều bạn muốn, thì bạn cần phải đến đó sớm và đặt cờ, nếu không thì chỉ cần đặt cờ mặc định.

Tôi chưa viết bất cứ điều gì để phản hồi cho người dùng bật hoặc tắt cờ. Vì vậy, đây là cơ bản mã ví dụ. Nếu chúng tôi muốn người dùng tương tác với điều này, tất cả các bộ điều khiển xem sẽ cần tải lại. Tôi không biết làm thế nào để làm điều đó một cách trực tiếp nhưng có lẽ gửi một số thông báo sẽ thực hiện thủ thuật. Vì vậy, ngay bây giờ, chế độ bật / tắt toàn cầu này cho chế độ tối chỉ hoạt động khi khởi động hoặc khởi động lại ứng dụng.

Bây giờ, nó không đủ để cố gắng tắt chế độ tối trong mỗi chế độ xem MFING duy nhất trong ứng dụng khổng lồ của bạn. Nếu bạn đang sử dụng tài sản màu, bạn hoàn toàn bị thu hút. Chúng tôi trong hơn 10 năm đã hiểu các đối tượng bất biến là bất biến. Màu sắc bạn nhận được từ danh mục tài sản màu cho biết chúng là UIColor nhưng chúng là màu động (có thể thay đổi) và sẽ thay đổi bên dưới bạn khi hệ thống thay đổi từ chế độ tối sang sáng. Đó được coi là một tính năng. Nhưng tất nhiên không có chủ nhân nào yêu cầu những điều này ngừng thực hiện thay đổi này (theo như tôi biết ngay bây giờ, có lẽ ai đó có thể cải thiện điều này).

Vì vậy, giải pháp là hai phần:

  1. một danh mục công khai trên UIViewControll cung cấp một số phương thức tiện ích và tiện lợi ... chẳng hạn, tôi không nghĩ rằng apple đã nghĩ về thực tế rằng một số người trong chúng ta trộn mã web vào các ứng dụng của mình. Như vậy, chúng ta có các bảng định kiểu cần được bật dựa trên chế độ tối hoặc sáng. Do đó, bạn cần phải xây dựng một số loại đối tượng biểu định kiểu động (sẽ tốt) hoặc chỉ hỏi trạng thái hiện tại là gì (xấu nhưng dễ).

  2. thể loại này khi tải sẽ thay thế phương thức viewDidLoad của lớp UIViewControll và chặn các cuộc gọi. Tôi không biết nếu điều đó phá vỡ quy tắc cửa hàng ứng dụng. Nếu có, có nhiều cách khác có lẽ nhưng bạn có thể coi đó là một bằng chứng về khái niệm. Ví dụ, bạn có thể tạo một lớp con của tất cả các loại trình điều khiển chế độ xem chính và làm cho tất cả các bộ điều khiển chế độ xem của riêng bạn được kế thừa từ các loại đó, sau đó bạn có thể sử dụng ý tưởng danh mục DarkMode và gọi nó để từ chối tất cả các bộ điều khiển xem của bạn. Nó xấu hơn nhưng nó sẽ không phá vỡ bất kỳ quy tắc nào. Tôi thích sử dụng thời gian chạy vì đó là những gì thời gian chạy được thực hiện. Vì vậy, trong phiên bản của tôi, bạn chỉ cần thêm danh mục, bạn đặt biến toàn cục trên danh mục cho dù bạn có muốn chặn chế độ tối hay không và nó sẽ làm điều đó.

  3. Bạn chưa ra khỏi rừng, như đã đề cập, vấn đề khác là UIColor về cơ bản làm bất cứ điều gì nó muốn. Vì vậy, ngay cả khi bộ điều khiển chế độ xem của bạn đang chặn chế độ tối, UIColor không biết bạn đang sử dụng nó ở đâu và như thế nào. Kết quả là bạn có thể tìm nạp nó một cách chính xác nhưng sau đó nó sẽ trở lại với bạn vào một lúc nào đó trong tương lai. Có lẽ sớm có thể muộn hơn. Vì vậy, cách đó là bằng cách phân bổ hai lần bằng CGColor và biến nó thành màu tĩnh. Điều này có nghĩa là nếu người dùng của bạn quay lại và bật lại chế độ tối trên trang cài đặt của bạn (ý tưởng ở đây là làm cho nó hoạt động để người dùng có quyền kiểm soát ứng dụng của bạn trên và trên phần còn lại của hệ thống), tất cả các màu tĩnh đó cần thay thế. Cho đến nay điều này là để cho người khác giải quyết. Cách dễ dàng để làm điều đó là tạo một mặc định rằng bạn ' đang từ chối chế độ tối, chia cho số 0 để làm hỏng ứng dụng vì bạn không thể thoát khỏi ứng dụng và yêu cầu người dùng khởi động lại nó. Điều đó có thể vi phạm nguyên tắc của cửa hàng ứng dụng, nhưng đó là một ý tưởng.

Danh mục UIColor không cần phải phơi bày, nó chỉ hoạt động khi gọi colorNamed: ... nếu bạn không nói với lớp DarkMode ViewContoder chặn chế độ tối, nó sẽ hoạt động hoàn hảo như mong đợi. Cố gắng làm cho một cái gì đó thanh lịch thay vì mã sphaghetti táo tiêu chuẩn, điều đó có nghĩa là bạn sẽ phải sửa đổi hầu hết ứng dụng của mình nếu bạn muốn lập trình từ chối chế độ tối hoặc chuyển đổi nó theo chương trình. Bây giờ tôi không biết có cách nào tốt hơn để thay đổi Info.plist theo chương trình để tắt chế độ tối khi cần. Theo như sự hiểu biết của tôi thì đó là một tính năng thời gian biên dịch và sau đó bạn được rút ra.

Vì vậy, đây là mã bạn cần. Nên thả vào và chỉ sử dụng một phương thức để đặt Kiểu UI hoặc đặt mặc định trong mã. Bạn có thể tự do sử dụng, sửa đổi, làm bất cứ điều gì bạn muốn với mục đích này và không có bảo hành nào được đưa ra và tôi không biết liệu nó có vượt qua cửa hàng ứng dụng hay không. Cải tiến rất đáng hoan nghênh.

Cảnh báo công bằng Tôi không sử dụng ARC hoặc bất kỳ phương pháp nắm giữ nào khác.

////// H file

#import <UIKit/UIKit.h>

@interface UIViewController(DarkMode)

// if you want to globally opt out of dark mode you call these before any view controllers load
// at the moment they will only take effect for future loaded view controllers, rather than currently
// loaded view controllers

// we are doing it like this so you don't have to fill your code with @availables() when you include this
typedef enum {
    QOverrideUserInterfaceStyleUnspecified,
    QOverrideUserInterfaceStyleLight,
    QOverrideUserInterfaceStyleDark,
} QOverrideUserInterfaceStyle;

// the opposite condition is light interface mode
+ (void)setOverrideUserInterfaceMode:(QOverrideUserInterfaceStyle)override;
+ (QOverrideUserInterfaceStyle)overrideUserInterfaceMode;

// utility methods
// this will tell you if any particular view controller is operating in dark mode
- (BOOL)isUsingDarkInterfaceStyle;
// this will tell you if any particular view controller is operating in light mode mode
- (BOOL)isUsingLightInterfaceStyle;

// this is called automatically during all view controller loads to enforce a single style
- (void)tryToOverrideUserInterfaceStyle;

@end


////// M file


//
//  QDarkMode.m

#import "UIViewController+DarkMode.h"
#import "q-runtime.h"


@implementation UIViewController(DarkMode)

typedef void (*void_method_imp_t) (id self, SEL cmd);
static void_method_imp_t _nativeViewDidLoad = NULL;
// we can't @available here because we're not in a method context
static long _override = -1;

+ (void)load;
{
#define DEFAULT_UI_STYLE UIUserInterfaceStyleLight
    // we won't mess around with anything that is not iOS 13 dark mode capable
    if (@available(iOS 13,*)) {
        // default setting is to override into light style
        _override = DEFAULT_UI_STYLE;
        /*
         This doesn't work...
        NSUserDefaults *d = NSUserDefaults.standardUserDefaults;
        [d setObject:@"Light" forKey:@"UIUserInterfaceStyle"];
        id uiStyle = [d objectForKey:@"UIUserInterfaceStyle"];
        NSLog(@"%@",uiStyle);
         */
        if (!_nativeViewDidLoad) {
            Class targetClass = UIViewController.class;
            SEL targetSelector = @selector(viewDidLoad);
            SEL replacementSelector = @selector(_overrideModeViewDidLoad);
            _nativeViewDidLoad = (void_method_imp_t)QMethodImplementationForSEL(targetClass,targetSelector);
            QInstanceMethodOverrideFromClass(targetClass, targetSelector, targetClass, replacementSelector);
        }
    }
}

// we do it like this because it's not going to be set often, and it will be tested often
// so we can cache the value that we want to hand to the OS
+ (void)setOverrideUserInterfaceMode:(QOverrideUserInterfaceStyle)style;
{
    if (@available(iOS 13,*)){
        switch(style) {
            case QOverrideUserInterfaceStyleLight: {
                _override = UIUserInterfaceStyleLight;
            } break;
            case QOverrideUserInterfaceStyleDark: {
                _override = UIUserInterfaceStyleDark;
            } break;
            default:
                /* FALLTHROUGH - more modes can go here*/
            case QOverrideUserInterfaceStyleUnspecified: {
                _override = UIUserInterfaceStyleUnspecified;
            } break;
        }
    }
}
+ (QOverrideUserInterfaceStyle)overrideUserInterfaceMode;
{
    if (@available(iOS 13,*)){
        switch(_override) {
            case UIUserInterfaceStyleLight: {
                return QOverrideUserInterfaceStyleLight;
            } break;
            case UIUserInterfaceStyleDark: {
                return QOverrideUserInterfaceStyleDark;
            } break;
            default:
                /* FALLTHROUGH */
            case UIUserInterfaceStyleUnspecified: {
                return QOverrideUserInterfaceStyleUnspecified;
            } break;
        }
    } else {
        // we can't override anything below iOS 12
        return QOverrideUserInterfaceStyleUnspecified;
    }
}

- (BOOL)isUsingDarkInterfaceStyle;
{
    if (@available(iOS 13,*)) {
        if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark){
            return YES;
        }
    }
    return NO;
}

- (BOOL)isUsingLightInterfaceStyle;
{
    if (@available(iOS 13,*)) {
        if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight){
            return YES;
        }
        // if it's unspecified we should probably assume light mode, esp. iOS 12
    }
    return YES;
}

- (void)tryToOverrideUserInterfaceStyle;
{
    // we have to check again or the compile will bitch
    if (@available(iOS 13,*)) {
        [self setOverrideUserInterfaceStyle:(UIUserInterfaceStyle)_override];
    }
}

// this method will be called via the viewDidLoad chain as we will patch it into the
// UIViewController class
- (void)_overrideModeViewDidLoad;
{
    if (_nativeViewDidLoad) {
        _nativeViewDidLoad(self,@selector(viewDidLoad));
    }
    [self tryToOverrideUserInterfaceStyle];
}


@end

// keep this in the same file, hidden away as it needs to switch on the global ... yeah global variables, I know, but viewDidLoad and colorNamed: are going to get called a ton and already it's adding some inefficiency to an already inefficient system ... you can change if you want to make it a class variable. 

// this is necessary because UIColor will also check the current trait collection when using asset catalogs
// so we need to repair colorNamed: and possibly other methods
@interface UIColor(DarkMode)
@end

@implementation UIColor (DarkMode)

typedef UIColor *(*color_method_imp_t) (id self, SEL cmd, NSString *name);
static color_method_imp_t _nativeColorNamed = NULL;
+ (void)load;
{
    // we won't mess around with anything that is not iOS 13 dark mode capable
    if (@available(iOS 13,*)) {
        // default setting is to override into light style
        if (!_nativeColorNamed) {
            // we need to call it once to force the color assets to load
            Class targetClass = UIColor.class;
            SEL targetSelector = @selector(colorNamed:);
            SEL replacementSelector = @selector(_overrideColorNamed:);
            _nativeColorNamed = (color_method_imp_t)QClassMethodImplementationForSEL(targetClass,targetSelector);
            QClassMethodOverrideFromClass(targetClass, targetSelector, targetClass, replacementSelector);
        }
    }
}


// basically the colors you get
// out of colorNamed: are dynamic colors... as the system traits change underneath you, the UIColor object you
// have will also change since we can't force override the system traits all we can do is force the UIColor
// that's requested to be allocated out of the trait collection, and then stripped of the dynamic info
// unfortunately that means that all colors throughout the app will be static and that is either a bug or
// a good thing since they won't respond to the system going in and out of dark mode
+ (UIColor *)_overrideColorNamed:(NSString *)string;
{
    UIColor *value = nil;
    if (@available(iOS 13,*)) {
        value = _nativeColorNamed(self,@selector(colorNamed:),string);
        if (_override != UIUserInterfaceStyleUnspecified) {
            // the value we have is a dynamic color... we need to resolve against a chosen trait collection
            UITraitCollection *tc = [UITraitCollection traitCollectionWithUserInterfaceStyle:_override];
            value = [value resolvedColorWithTraitCollection:tc];
        }
    } else {
        // this is unreachable code since the method won't get patched in below iOS 13, so this
        // is left blank on purpose
    }
    return value;
}
@end

Có một tập hợp các hàm tiện ích mà điều này sử dụng để thực hiện hoán đổi phương thức. Tập tin riêng biệt. Đây là công cụ tiêu chuẩn mặc dù và bạn có thể tìm thấy mã tương tự ở bất cứ đâu.

// q-runtime.h

#import <Foundation/Foundation.h>
#import <objc/message.h>
#import <stdatomic.h>

// returns the method implementation for the selector
extern IMP
QMethodImplementationForSEL(Class aClass, SEL aSelector);

// as above but gets class method
extern IMP
QClassMethodImplementationForSEL(Class aClass, SEL aSelector);


extern BOOL
QClassMethodOverrideFromClass(Class targetClass, SEL targetSelector,
                              Class replacementClass, SEL replacementSelector);

extern BOOL
QInstanceMethodOverrideFromClass(Class targetClass, SEL targetSelector,
                                 Class replacementClass, SEL replacementSelector);


// q-runtime.m

static BOOL
_QMethodOverride(Class targetClass, SEL targetSelector, Method original, Method replacement)
{
    BOOL flag = NO;
    IMP imp = method_getImplementation(replacement);
    // we need something to work with
    if (replacement) {
        // if something was sitting on the SEL already
        if (original) {
            flag = method_setImplementation(original, imp) ? YES : NO;
            // if we're swapping, use this
            //method_exchangeImplementations(om, rm);
        } else {
            // not sure this works with class methods...
            // if it's not there we want to add it
            flag = YES;
            const char *types = method_getTypeEncoding(replacement);
            class_addMethod(targetClass,targetSelector,imp,types);
            XLog_FB(red,black,@"Not sure this works...");
        }
    }
    return flag;
}

BOOL
QInstanceMethodOverrideFromClass(Class targetClass, SEL targetSelector,
                                 Class replacementClass, SEL replacementSelector)
{
    BOOL flag = NO;
    if (targetClass && replacementClass) {
        Method om = class_getInstanceMethod(targetClass,targetSelector);
        Method rm = class_getInstanceMethod(replacementClass,replacementSelector);
        flag = _QMethodOverride(targetClass,targetSelector,om,rm);
    }
    return flag;
}


BOOL
QClassMethodOverrideFromClass(Class targetClass, SEL targetSelector,
                              Class replacementClass, SEL replacementSelector)
{
    BOOL flag = NO;
    if (targetClass && replacementClass) {
        Method om = class_getClassMethod(targetClass,targetSelector);
        Method rm = class_getClassMethod(replacementClass,replacementSelector);
        flag = _QMethodOverride(targetClass,targetSelector,om,rm);
    }
    return flag;
}

IMP
QMethodImplementationForSEL(Class aClass, SEL aSelector)
{
    Method method = class_getInstanceMethod(aClass,aSelector);
    if (method) {
        return method_getImplementation(method);
    } else {
        return NULL;
    }
}

IMP
QClassMethodImplementationForSEL(Class aClass, SEL aSelector)
{
    Method method = class_getClassMethod(aClass,aSelector);
    if (method) {
        return method_getImplementation(method);
    } else {
        return NULL;
    }
}

Tôi đang sao chép và dán tệp này ra khỏi một vài tệp vì q-runtime.h là thư viện có thể sử dụng lại của tôi và đây chỉ là một phần của nó. Nếu một cái gì đó không biên dịch cho tôi biết.


Bạn sẽ không gặp may khi kiểm soát hành vi của UIColor, như đã thảo luận trong câu hỏi này: stackoverflow.com/questions/56487679/
Kẻ
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.