iOS 13에서 다크 모드를 옵트 아웃 할 수 있습니까?


297

내 응용 프로그램의 대부분은 웹 구현으로 구성되어 기본 구현으로는 아직 사용할 수없는 기능을 제공합니다. 웹 팀은 웹 사이트에 어두운 테마를 구현할 계획이 없습니다. 따라서 내 앱은 iOS 13에서 다크 모드를 지원하면 절반의 절반으로 보입니다.

앱이 웹 사이트 테마와 일치하도록 항상 라이트 모드를 표시하도록 다크 모드 지원을 선택 해제 할 수 있습니까?


71
설정 UIUserInterfaceStyleLight당신의 Info.plist에. developer.apple.com/library/archive/documentation/General/…을
Tieme

1
물어 주셔서 감사합니다-우리 모두를 위해. 많은 앱이 필요합니다. 전환이 준비 될 때까지 앱을 계속 작동시키는 데 필요합니다.
user3741598

import Foundation import UIKit 확장 UIViewController {open func arideFromNib () {super.awakeFromNib () if #available (iOS 13.0, *) {// 항상 가벼운 인터페이스 스타일을 채택하십시오. overrideUserInterfaceStyle = .light}}}
Mohammad Razipour

1
plist 에 UIUserInterfaceStyle 을 추가하기 만하면 됩니다. 그것은 쉽다
Fattie

앱을 앱 스토어에 제출하는 동안 라이트 모드의 UIUserInterfaceStyle로 인해 Apple 수락이 수행됩니다.
kiran

답변:


684

첫째, 다음은 어두운 모드 선택 해제와 관련된 Apple의 항목 입니다. 이 링크의 내용은 Xcode 11 및 iOS 13 용으로 작성되었습니다 .

이 섹션은 Xcode 11 사용법에 적용됩니다


ENTIRE 신청을 거부하려면

접근법 # 1

info.plist 파일 에서 다음 키를 사용하십시오 .

UIUserInterfaceStyle

그리고 값을 할당하십시오 Light.

XML 에 대한 UIUserInterfaceStyle할당 :

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

접근법 # 2

overrideUserInterfaceStyle앱의 window변수 에 대해 설정할 수 있습니다 .

프로젝트 생성 방법에 따라 AppDelegate파일 또는 에있을 수 있습니다 SceneDelegate.

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


UIViewController를 개별적으로 선택 해제하려면

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

overrideUserInterfaceStyle에 대한 Apple 문서

위의 코드는 Xcode 11에서 어떻게 보일까요?

여기에 이미지 설명을 입력하십시오

이 섹션은 Xcode 10.x 사용법에 적용됩니다


제출에 Xcode 11을 사용하는 경우이 줄 아래의 모든 내용을 무시해도됩니다.

iOS 12에는 관련 API가 없으므로 위에 제공된 값을 사용하려고하면 오류가 발생합니다.

overrideUserInterfaceStyle귀하의 설정 에UIViewController

여기에 이미지 설명을 입력하십시오

UIViewController를 개별적으로 선택 해제하려면

컴파일러 버전과 iOS 버전을 테스트하여 Xcode 10에서 처리 할 수 ​​있습니다.

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

ENTIRE 신청을 거부하려면

AppDelegate파일에 다음 코드를 추가하여 위의 스 니펫을 수정하여 Xcode 10의 전체 응용 프로그램에서 작동하도록 할 수 있습니다.

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

그러나 Xcode 버전 10.x를 사용하는 경우 plist 설정이 실패합니다.

여기에 이미지 설명을 입력하십시오

에 신용 @Aron 넬슨 , @Raimundas Sakalauskas , @NSLeaderrmaddy 그들의 피드백이 답변을 향상.


2
지금 앱을 업데이트 / 업로드 할 때 UIUserInterfaceStyle 표시등이 차단됩니다. 유효하지 않은 plist 항목으로 표시됩니다. (잘못된 plist 키)
Aron Nelson

2
iOS SDK 12 (현재 최신 안정 SDK)에 대해 컴파일되지 않습니다. iOS 12 SDK에서도 작동하는 솔루션 은 stackoverflow.com/a/57521901/2249485 를 참조하십시오 .
Raimundas Sakalauskas '

이것은 "부정한 질문"보다 훨씬 더 많은 견해를 가진 질문이 답변을 제공하기 위해 잠겨 있기에는 불공평합니다. :(
Raimundas Sakalauskas

7
대신 설정의 overrideUserInterfaceStyle에서 viewDidLoad모든 뷰 컨트롤러, 당신은 응용 프로그램의 주 창에서 한 번에 설정할 수 있습니다. 전체 앱이 한 방향으로 동작하도록하려면 훨씬 쉽습니다.
rmaddy

2
사용 #if compiler(>=5.1)대신 responds(to:)setValue
NSLeader

162

"iOS에서 다크 모드 구현"( https://developer.apple.com/videos/play/wwdc2019/214/ 31:13에서 시작) 에 대한 Apple의 세션에 따르면 모든 뷰 컨트롤러 또는 뷰에서 설정 하거나 설정할 overrideUserInterfaceStyle수 있습니다 하위 뷰 또는 뷰 컨트롤러에 사용될 것 입니다.UIUserInterfaceStyleLightUIUserInterfaceStyleDarktraitCollection

이미 SeanR에서 언급 한 바와 같이, 당신은 설정할 수 UIUserInterfaceStyleLight또는 Dark전체 앱이 변경 앱을 plist 파일에.


17
UIUserInterfaceStyle 키를 설정하면 앱 스토어에서 앱이 거부됩니다
Sonius

2
Apple은 ITMS-90190 오류 코드 forums.developer.apple.com/thread/121028
PRASAD1240

11
iOS 13 SDK가 아직 베타 버전이 아니기 때문에 거부 가능성이 높습니다. Xcode 11 GM을 사용할 수있게 되 자마자 이것이 효과가 있다고 생각합니다.
dorbeetle

2
@dorbeetle 사실이 아닙니다 .Xcode 10을 사용하여 1 개월 전과 같이이 키로 내 앱을 성공적으로 업로드했습니다. 최근 거부가 발생했습니다. 새로운 애플 전략의 일종 인 것 같습니다.
steven

1
여전히 일어나고 있습니다. Xcode GM2가 앱 서명 오류를 반환했습니다. Xcode 10.3에서 "유효하지 않은 Info.plist 키. Payload / Galileo.appInfo.plist 파일의 'UIUserInterfaceStyle'키가 유효하지 않습니다."
Evgen Bodunov

64

Xcode 11 이상 (iOS 13 이상 SDK)을 사용하지 않는 경우 앱이 자동으로 다크 모드를 지원하지 않도록 선택했습니다. 따라서 어두운 모드를 선택 해제 할 필요가 없습니다.

Xcode 11 이상을 사용하는 경우 시스템에서 앱에 대해 어두운 모드가 자동으로 활성화되었습니다. 선호도에 따라 어두운 모드를 비활성화하는 두 가지 방법이 있습니다. 특정 창,보기 또는보기 컨트롤러에 대해 완전히 비활성화하거나 비활성화 할 수 있습니다.

앱에서 다크 모드를 완전히 비활성화

앱의 Info.plist 파일에서 UIUserInterfaceStyle와 같이 키를 값으로 포함하여 어두운 모드를 비활성화 할 수 있습니다 Light. 사용자의 선호도를 무시하고 항상 앱에 밝은 모양을 적용합니다.
빛으로 UIUserInterfaceStyle

Window, View 또는 View Controller에 대해 어두운 모드 비활성화

overrideUserInterfaceStyle적절한 창, 뷰 또는 뷰 컨트롤러 의 속성을 설정하여 인터페이스가 항상 밝거나 어두운 스타일로 표시되도록 할 수 있습니다 .

컨트롤러보기 :

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

견해:

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

창문:

/* 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

참고 : Apple은 앱에서 다크 모드를 지원할 것을 적극 권장합니다. 따라서 일시적으로 어두운 모드 만 비활성화 할 수 있습니다.

자세한 내용은 여기를 참조하십시오 : iOS 앱을위한 특정 인터페이스 스타일 선택


34

********** Xcode 11 이상을위한 가장 쉬운 방법 ***********

전에 info.plist에 추가하십시오 </dict></plist>

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

이 솔루션은 엑스 코드 10.x에서의 응용 프로그램을 제출에서 때를 실패합니다
타우 픽 Bouabid

27

해결책을 찾았습니다. 나는 처음에 UIUserInterfaceStyle-Information Property ListUIUserInterfaceStyle-UIKit 에서 한 조각을 만들었지 만, 실제로 iOS 앱을위한 특정 인터페이스 스타일 선택에 문서화되어있는 것을 발견했다 .

당신의에서 info.plist설정 UIUserInterfaceStyle( 사용자 인터페이스 스타일 에) 1 ( UIUserInterfaceStyle.light).

편집 : dorbeetle의 답변에 따라 더 적합한 설정 UIUserInterfaceStyle은입니다 Light.


값을 2로 설정하여 어두운 모드를 적용하면 작동하지 않습니다.[UIInterfaceStyle] '2' is not a recognized value for UIUserInterfaceStyle. Defaulting to Light.
funkenstrahlen

3
plist에이 키가 있으면 App Store가 거부됩니다.
José

1
AppStore는 더 이상 plist.info에서이 속성을 거부하지 않습니다. 앱이 이미 어둡기 때문에 "Dark"(대문자)를 넣었습니다. 아무 문제 없습니다. 이를 통해 시스템 컨트롤을 올바르게 사용할 수 있습니다.
nickdnk

@nickdnk 애플이 권장하는 Xcode 11로 앱을 빌드했다고 생각합니다.
DawnSong

1
그래, 내가 했어. 애플이 plist 에서이 매개 변수를 허용한다는 사실을 변경하지는 않습니다.
nickdnk

23

위의 답변은 전체 앱을 선택 해제하려는 경우 작동합니다. UI가있는 lib에서 작업 중이고 .plist를 편집 할 권한이없는 경우 코드를 통해 수행 할 수도 있습니다.

iOS 13 SDK에 대해 컴파일하는 경우 다음 코드를 간단히 사용할 수 있습니다.

빠른:

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

Obj-C :

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

그러나 iOS 12 SDK 에 대해 코드를 컴파일하려면 (현재 여전히 안정적인 최신 SDK 임) 선택기를 사용해야합니다. 선택기가있는 코드 :

Swift (XCode는이 코드에 대한 경고를 표시하지만 SDK 12에 속성이 없으므로 컴파일하지 않기 때문에 지금이 유일한 방법입니다) :

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"];
    }
}

속성이 overrideUserInterfaceStyle속한 것을 지정하면 더 좋습니다 .
DawnSong

12

최근 업데이트-

Xcode 10.x를 사용하는 경우 기본값 UIUserInterfaceStylelightiOS 13.x입니다. iOS 13 기기에서 실행하면 기기는 라이트 모드에서만 작동합니다.

UIUserInterfaceStyleInfo.plist 파일에 키 를 명시 적으로 추가 할 필요가 없습니다. 키를 추가하면 앱을 확인할 때 오류가 발생합니다.

잘못된 Info.plist 키입니다. Payload / AppName.appInfo.plist 파일의 'UIUserInterfaceStyle'키가 유효하지 않습니다.

UIUserInterfaceStyleXcode 11.x를 사용할 때는 Info.plist 파일에 키만 추가하십시오 .


1
Xcode 10 또는 11과는 아무런 관련이 없습니다. 사용자가 Xcode 10 형식의 응용 프로그램을 배포하고 어두운 모드를 처리하지 않으면 iPhone 11, Pro 또는 Pro Max에 설치하면 어두운 모드 문제가 발생합니다. Xcode 11로 업데이트하고이 문제를 해결해야합니다.
Niranjan Molkeri

3
@NiranjanMolkeri 이것은 새로운 아이폰과는 아무런 관련이 없습니다. iOS 13의 다크 모드에 관한 것입니다. 이전 iOS 13 베타 버전의 앱에서 UI를 명시 적으로 처리하지 않으면 다크 모드 문제가 발생했습니다. 그러나 최신 버전에서는 수정되었습니다. XCode 10을 사용하는 경우 기본 UIUserInterfaceStyle은 iOS13에서 약합니다. Xode11을 사용하는 경우이를 처리해야합니다.
kumarsiddharth123

Xcode 10.3을 사용하여 TestFligth에 앱을 업로드하고 plist에 주요 UIUserInterfaceStyle이 포함 된 경우 문제가 발생합니다. 유효하지 않은 plist 파일이라고합니다. Xcode 10에서 빌드하거나 Xcode 11
eharo2를

9

UIUserInterfaceStyleplist 파일 에 키를 추가 하면 Apple은 https://stackoverflow.com/a/56546554/7524146에서 언급 한 것처럼 릴리스 빌드를 거부 할 수 있습니다. 어쨌든 각 ViewController 를 명시 적으로 말하면 귀찮습니다self.overrideUserInterfaceStyle = .light . 그러나 루트 window객체 에이 코드의 평화를 한 번 사용할 수 있습니다 .

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

application(application: didFinishLaunchingWithOptions:)이 선택기는 true초기 단계에서 응답하지 않기 때문에이 작업을 수행 할 수 없습니다 . 그러나 나중에 할 수 있습니다. AppDelegate에서 UI를 자동으로 시작하는 대신 앱에서 사용자 정의 AppPresenter또는 AppRouter클래스를 사용하면 매우 쉽습니다 .


9

Xcode 11의 전체 응용 프로그램에서 다크 모드를 끌 수 있습니다 .

  1. Info.plist로 이동
  2. 벨로우즈 추가

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

Info.plist는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오


1
어떤 이유로 Xcode 버전 11.3.1 (11C504)에서 작동하지 않음
Andrew

7

-전체 앱 (창) :

window!.overrideUserInterfaceStyle = .light

당신은에서 창을 얻을 수 있습니다 SceneDelegate

-단일 ViewController의 경우 :

viewController.overrideUserInterfaceStyle = .light

viewControllerviewController 내부에서도 자체를 설정할 수 있습니다.

-단일 뷰의 경우 :

view.overrideUserInterfaceStyle = .light

view보기 내부에서도 자체를 설정할 수 있습니다.

if #available(iOS 13.0, *) { ,,, }이전 iOS 버전을 지원하는 경우 사용해야 할 수 있습니다.


6

다른 응답 외에도 다음에 대한 나의 이해를 제외하고는 iOS 13 SDK (XCode 11 사용)에 대해 컴파일 할 때 다크 모드 만 준비하면됩니다.

시스템은 iOS 13 이상 SDK에 연결된 앱이 밝고 어두운 모양을 모두 지원한다고 가정합니다. iOS에서는 특정 인터페이스 스타일을 창,보기 또는보기 컨트롤러에 지정하여 원하는 모양을 지정합니다. Info.plist 키를 사용하여 다크 모드 지원을 완전히 비활성화 할 수도 있습니다.

링크


2

예, viewDidLoad에 다음 코드를 추가하여 건너 뛸 수 있습니다.

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

2

내 앱은 현재 어두운 모드를 지원하지 않으며 밝은 앱 바 색상을 사용합니다. 내 키에 다음 키를 추가하여 상태 표시 줄 내용을 어두운 텍스트 및 아이콘으로 강제 설정할 수있었습니다 Info.plist.

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

다른 가능한 값은 여기 ( https://developer.apple.com/documentation/uikit/uistatusbarstyle) 에서 찾으십시오.


2

Objective-C 버전

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

1

어두운 모드를 지원하거나 우회하기 위해 앱에서 사용할 수있는 몇 가지 팁과 요령이 있습니다.

첫 번째 팁 : ViewController 스타일을 재정의하려면

UIViewController의 인터페이스 스타일을 재정의 할 수 있습니다

1 : overrideUserInterfaceStyle = .dark // 다크 모드의 경우

2 : overrideUserInterfaceStyle = .light // 라이트 모드

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

두 번째 팁 : info.plist에 키 추가

간단히 새 키를 추가 할 수 있습니다

UIUserInterfaceStyle

app info.plist에서 값을 Light 또는 Dark로 설정하십시오. 앱 기본 스타일을 제공 한 값으로 재정의합니다. 모든 viewController에 overrideUserInterfaceStyle = .light를 추가 할 필요는 없습니다. info.plist에 한 줄만 있으면됩니다.


1

단순히 info.plist파일 에 다음 키를 추가 하십시오.

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

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

코드 전용 답변을 게시하는 대신이 답변이 어떻게 문제를 해결하는지 설명 할 수 있습니다.
Arun Vinoth

그래도 @ArunVinoth IOS 13에서 다크 모드가 도입되었으므로 배포 대상이 13보다 낮 으면 위 코드를 사용하고 그렇지 않으면 if 블록으로 작성된 간단한 문장을 사용할 수 있습니다.
Talha Rasool

1

스위프트 5

어두운 모드에서 밝은 모드로 전환하는 두 가지 방법 :

1-info.plist

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

2- 프로그래밍 방식으로

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

0

앱 수명주기 동안 창 속성이 변경 될 수 있으므로이 솔루션을 사용합니다. 따라서 "overrideUserInterfaceStyle = .light"할당을 반복해야합니다. UIWindow.appearance ()를 사용하면 새로 만든 UIWindow 객체에 사용될 기본값을 설정할 수 있습니다.

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

info.plist 파일에 다음 줄을 추가하십시오.

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

이렇게하면 앱이 라이트 모드에서만 실행됩니다.


이것은 이미 여러 차례 언급되고 답변되었습니다. 받아 들여진 대답조차도 이것을 제안하고 있습니다. 따라서이 의견은 새로운 정보를 추가하지 않습니다.
JeroenJK

0
import UIKit

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}

2
다른 사람들이 배울 수 있도록 답변을 수정하여 답변에 설명을 추가해주세요.
Nico Haase

0

이 새로운 키 UIUserInterfaceStyle을 Info.plist에 추가하고 그 값을 Light로 설정하십시오. 경고 컨트롤러가 표시 등 모드로 나타나는지 확인하십시오.

UIUserInterfaceStyle Light Info.plist 파일에 UIUserInterfaceStyle 키를 추가하고 그 값을 Light 또는 Dark로 설정하여 사용자 설정에 관계없이 전체 응용 프로그램에서 강제 명암 모드 인 경우 값을 밝게 또는 어둡게합니다.


0

이 질문에는 많은 답변이 있습니다. 대신에 사용하면 info.plist다음 AppDelegate과 같이 설정할 수 있습니다 .

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

Xcode 11.3, iOS 13.3에서 테스트


-8

실제로 나는 방금 응용 프로그램의 모든 단일 viw 컨트롤러와 함께 putz하지 않고도 코드에서 암 모드를 선택적으로 해제 할 수있는 코드를 작성했습니다. 클래스 목록을 관리하여 클래스별로 선택 해제하도록 조정할 수 있습니다. 내가 원하는 것은 사용자가 내 앱의 어두운 모드 인터페이스를 좋아하는지 확인하고 마음에 들지 않으면 끌 수 있다는 것입니다. 이렇게하면 나머지 응용 프로그램에 계속 어두운 모드를 사용할 수 있습니다.

사용자의 선택이 좋습니다 (Ahem, Apple을 보았을 때,이를 구현해야했습니다).

이것이 작동하는 방식은 UIViewController의 범주 일뿐입니다. 로드되면 기본 viewDidLoad 메소드를 전역 플래그를 검사하여 모든 모드에서 다크 모드가 비활성화되었는지 여부를 확인하는 메소드로 바꿉니다.

UIViewController로드시 트리거되므로 기본적으로 자동으로 시작되어 어두운 모드를 비활성화해야합니다. 이것이 원하는 것이 아니라면, 어딘가에 일찍 도착하여 깃발을 설정하거나 기본 깃발을 설정해야합니다.

플래그를 켜거나 끄는 사용자에게 응답 할 내용을 아직 작성하지 않았습니다. 이것은 기본적으로 예제 코드입니다. 사용자가 이것과 상호 작용하도록하려면 모든 뷰 컨트롤러를 다시로드해야합니다. 나는 그것을 직접하는 방법을 모르지만 아마도 알림을 보내면 트릭을 할 것입니다. 따라서 현재 어두운 모드에 대한이 전역 켜기 / 끄기는 앱을 시작하거나 다시 시작할 때만 작동합니다.

이제 거대한 앱의 모든 MFING viewController에서 어두운 모드를 끄는 것만으로는 충분하지 않습니다. 색상 자산을 사용하는 경우 완전히 뼈대가됩니다. 우리는 10 년 이상 불변의 물체가 불변이라는 것을 이해했습니다. 색상 자산 카탈로그에서 가져온 색상은 UIColor라고하지만 동적 (변경 가능) 색상이며 시스템이 어두운 모드에서 밝은 모드로 변경 될 때 사용자 아래에서 변경됩니다. 그것은 기능이어야합니다. 그러나 물론이 변경을 중단하도록 요청하는 마스터 토글은 없습니다 (지금 내가 아는 한 누군가가 이것을 향상시킬 수 있습니다).

따라서 솔루션은 두 부분으로 구성됩니다.

  1. 유틸리티와 편리한 메소드를 제공하는 UIViewController의 공개 카테고리. 따라서 어둡거나 밝은 모드를 기반으로 전환해야하는 스타일 시트가 있습니다. 따라서 어떤 종류의 동적 스타일 시트 객체 (좋은 것)를 만들거나 현재 상태가 무엇인지 (나쁘지만 쉬운 지) 물어보십시오.

  2. 로드 될 때이 카테고리는 UIViewController 클래스의 viewDidLoad 메소드를 대체하고 호출을 인터셉트합니다. 그것이 앱 스토어 규칙을 어기는지 모르겠습니다. 그렇다면, 그 주위에 다른 방법이 있지만 개념 증명이라고 생각할 수 있습니다. 예를 들어 모든 기본 뷰 컨트롤러 유형의 하위 클래스를 하나 만들고 모든 자체 뷰 컨트롤러를 상속 할 수 있습니다. 그런 다음 DarkMode 범주 아이디어를 사용하여 호출하여 모든 뷰 컨트롤러를 강제로 옵트 아웃 할 수 있습니다. 추악하지만 규칙을 어 기지 않을 것입니다. 런타임을 사용하기 때문에 런타임을 사용하는 것이 좋습니다. 따라서 내 버전에서는 범주를 추가하기 만하면 어두운 모드를 차단할지 여부에 대한 범주에 전역 변수를 설정하면됩니다.

  3. 이미 언급했듯이 다른 문제는 UIColor가 기본적으로 원하는 모든 것을 수행한다는 것입니다. 따라서 뷰 컨트롤러가 어두운 모드를 차단하더라도 UIColor는 사용하는 위치 또는 방법을 알지 못하므로 적응할 수 없습니다. 결과적으로 올바르게 가져올 수 있지만 나중에 언젠가 다시 되돌릴 것입니다. 아마 곧 나중에 따라서 CGColor를 사용하여 두 번 할당하고 정적 색상으로 바꾸는 것이 그 방법입니다. 이는 사용자가 설정 페이지에서 어두운 모드를 다시 활성화하고 다시 활성화하는 경우 (여기서는 사용자가 시스템의 나머지 부분 이상으로 앱을 제어 할 수 있도록이 작업을 수행하는 것임) 모든 정적 색상을 의미합니다. 교체해야합니다. 지금까지 다른 사람이 해결할 수 있습니다. 가장 쉬운 방법은 기본 설정을하는 것입니다. 어두운 모드를 해제하려면 앱을 종료하고 다시 시작하도록 할 수 없으므로 앱을 중단하려면 0으로 나눕니다. 아마도 앱 스토어 지침을 위반했을 수도 있지만 아이디어입니다.

UIColor 범주는 노출 될 필요가 없으며 colorNamed 호출 만 작동합니다. ... DarkMode ViewController 클래스에 어두운 모드를 차단하도록 지시하지 않으면 예상대로 완벽하게 작동합니다. 표준 애플 스파게티 코드 대신 우아한 무언가를 만들려고하면 프로그래밍 방식으로 어두운 모드를 선택 해제하거나 전환하려면 대부분의 앱을 수정해야합니다. 이제 필요에 따라 다크 모드를 끄도록 Info.plist를 프로그래밍 방식으로 변경하는 더 좋은 방법이 있는지 모르겠습니다. 내가 이해하는 한 그것은 컴파일 타임 기능이며 그 후에는 뼈가 생겼습니다.

필요한 코드는 다음과 같습니다. UI 스타일을 설정하거나 코드에서 기본값을 설정하려면 한 가지 방법 만 사용하십시오. 당신은 어떤 목적 으로든 자유롭게 사용, 수정, 원하는 것을 할 수 있으며 보증이 제공되지 않으며 앱 스토어를 통과하는지 여부를 모르겠습니다. 개선은 매우 환영합니다.

공정한 경고 나는 ARC 나 다른 핸드 홀딩 방법을 사용하지 않습니다.

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

메소드 스와핑을 수행하는 데 사용하는 유틸리티 함수 세트가 있습니다. 별도의 파일. 이것은 표준적인 내용이며 어디에서나 유사한 코드를 찾을 수 있습니다.

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

q-runtime.h가 재사용 가능한 라이브러리이므로이 파일의 일부에 불과하므로이 파일을 복사하여 붙여 넣습니다. 컴파일되지 않은 것이 있으면 알려주세요.


이 질문에 설명 된대로 UIColor 동작을 제어 할 때는 운이
나쁘지 않습니다
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.