├── .gitignore ├── Examples ├── DayNightDemo │ ├── DayNightDemo.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── DayNightDemo.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── DayNightDemo │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── 24_rijian.imageset │ │ │ │ ├── 24_rijian@2x.png │ │ │ │ ├── 24_rijian@3x.png │ │ │ │ └── Contents.json │ │ │ ├── 24_yejian.imageset │ │ │ │ ├── 24_yejian@2x.png │ │ │ │ ├── 24_yejian@3x.png │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── LVThemeKitExtension.swift │ │ └── ViewController.swift │ ├── Podfile │ └── Podfile.lock ├── DayNightDemoSwift │ ├── DayNightDemoSwift.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── DayNightDemoSwift.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── DayNightDemoSwift │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── 24_rijian.imageset │ │ │ │ ├── 24_rijian@2x.png │ │ │ │ ├── 24_rijian@3x.png │ │ │ │ └── Contents.json │ │ │ ├── 24_yejian.imageset │ │ │ │ ├── 24_yejian@2x.png │ │ │ │ ├── 24_yejian@3x.png │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── LVThemeKitExtension.swift │ │ └── ViewController.swift │ ├── Podfile │ └── Podfile.lock └── MultiThemesDemo │ ├── LVThemeKit.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── LVThemeKit.xcscheme │ ├── LVThemeKit.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── LVThemeKit │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Category │ │ ├── LVThemeColor+Demo.h │ │ ├── LVThemeColor+Demo.m │ │ ├── LVThemeKit+Demo.h │ │ ├── LVThemeKit+Demo.m │ │ ├── LVThemeString+Demo.h │ │ └── LVThemeString+Demo.m │ ├── Info.plist │ ├── ThemeManager │ │ ├── AppThemeManager.h │ │ ├── AppThemeManager.m │ │ ├── QDThemeManager.h │ │ ├── QDThemeManager.m │ │ ├── ReaderThemeManager.h │ │ ├── ReaderThemeManager.m │ │ ├── ThemeManager.h │ │ └── ThemeManager.m │ ├── UIViewWithLayer.h │ ├── UIViewWithLayer.m │ ├── Util │ │ ├── UIColor+hex.h │ │ └── UIColor+hex.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m │ ├── Podfile │ └── Podfile.lock ├── LICENSE ├── LVThemeKit.podspec ├── README.md ├── Source ├── Objc │ ├── CALayer+LVThemeKit.h │ ├── CALayer+LVThemeKit.m │ ├── CAShapeLayer+LVThemeKit.h │ ├── CAShapeLayer+LVThemeKit.m │ ├── LVThemeAttributedString.h │ ├── LVThemeAttributedString.m │ ├── LVThemeColor.h │ ├── LVThemeColor.m │ ├── LVThemeImage.h │ ├── LVThemeImage.m │ ├── LVThemeKit.h │ ├── LVThemeKit.m │ ├── LVThemeResource.h │ ├── LVThemeResource.m │ ├── LVThemeString.h │ ├── LVThemeString.m │ ├── NSString+LVThemeKit.h │ ├── NSString+LVThemeKit.m │ ├── UIButton+LVThemeKit.h │ ├── UIButton+LVThemeKit.m │ ├── UIImageView+LVThemeKit.h │ ├── UIImageView+LVThemeKit.m │ ├── UILabel+LVThemeKit.h │ ├── UILabel+LVThemeKit.m │ ├── UISlider+LVThemeKit.h │ ├── UISlider+LVThemeKit.m │ ├── UIView+LVThemeKit.h │ └── UIView+LVThemeKit.m └── Swift │ ├── CALayerTheme.swift │ ├── CAShapeLayerTheme.swift │ ├── LVThemeKit.swift │ ├── LVThemeResource.swift │ ├── UIButtonTheme.swift │ ├── UIImageViewTheme.swift │ ├── UILabelTheme.swift │ ├── UISliderTheme.swift │ └── UIViewTheme.swift └── oneclick.sh /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/xcode,objective-c,swift 3 | 4 | SDKs/ 5 | 6 | ### Xcode ### 7 | # Xcode 8 | # 9 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 10 | 11 | ## Build generated 12 | build/ 13 | DerivedData/ 14 | 15 | ## Various settings 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata/ 25 | 26 | ## Other 27 | *.moved-aside 28 | *.xccheckout 29 | *.xcscmblueprint 30 | 31 | 32 | ### Objective-C ### 33 | # Xcode 34 | # 35 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 36 | 37 | ## Build generated 38 | build/ 39 | DerivedData/ 40 | 41 | ## Various settings 42 | *.pbxuser 43 | !default.pbxuser 44 | *.mode1v3 45 | !default.mode1v3 46 | *.mode2v3 47 | !default.mode2v3 48 | *.perspectivev3 49 | !default.perspectivev3 50 | xcuserdata/ 51 | 52 | ## Other 53 | *.moved-aside 54 | *.xcuserstate 55 | 56 | ## Obj-C/Swift specific 57 | *.hmap 58 | *.ipa 59 | *.dSYM.zip 60 | *.dSYM 61 | 62 | # CocoaPods 63 | # 64 | # We recommend against adding the Pods directory to your .gitignore. However 65 | # you should judge for yourself, the pros and cons are mentioned at: 66 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 67 | # 68 | Pods/ 69 | 70 | # Carthage 71 | # 72 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 73 | 74 | Carthage/Checkouts 75 | Carthage/Build 76 | 77 | # fastlane 78 | # 79 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 80 | # screenshots whenever they are needed. 81 | # For more information about the recommended setup visit: 82 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 83 | 84 | fastlane/report.xml 85 | fastlane/Preview.html 86 | fastlane/screenshots 87 | fastlane/test_output 88 | 89 | # Code Injection 90 | # 91 | # After new code Injection tools there's a generated folder /iOSInjectionProject 92 | # https://github.com/johnno1962/injectionforxcode 93 | 94 | iOSInjectionProject/ 95 | 96 | ### Objective-C Patch ### 97 | *.xcscmblueprint 98 | 99 | 100 | ### Swift ### 101 | # Xcode 102 | # 103 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 104 | 105 | ## Build generated 106 | build/ 107 | DerivedData/ 108 | 109 | ## Various settings 110 | *.pbxuser 111 | !default.pbxuser 112 | *.mode1v3 113 | !default.mode1v3 114 | *.mode2v3 115 | !default.mode2v3 116 | *.perspectivev3 117 | !default.perspectivev3 118 | xcuserdata/ 119 | 120 | ## Other 121 | *.moved-aside 122 | *.xcuserstate 123 | 124 | ## Obj-C/Swift specific 125 | *.hmap 126 | *.ipa 127 | *.dSYM.zip 128 | *.dSYM 129 | 130 | ## Playgrounds 131 | timeline.xctimeline 132 | playground.xcworkspace 133 | 134 | # Swift Package Manager 135 | # 136 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 137 | # Packages/ 138 | .build/ 139 | 140 | # Carthage 141 | # 142 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 143 | # Carthage/Checkouts 144 | 145 | Carthage/Build 146 | 147 | # fastlane 148 | # 149 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 150 | # screenshots whenever they are needed. 151 | # For more information about the recommended setup visit: 152 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 153 | 154 | fastlane/report.xml 155 | fastlane/Preview.html 156 | fastlane/screenshots 157 | fastlane/test_output 158 | 159 | .DS_Store 160 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6298907890973D1D843456F7 /* Pods_DayNightDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18A5831482BF1A87D4FE09A8 /* Pods_DayNightDemo.framework */; }; 11 | E69DFCF2225C6A5C00E77C71 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69DFCF1225C6A5C00E77C71 /* AppDelegate.swift */; }; 12 | E69DFCF4225C6A5C00E77C71 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69DFCF3225C6A5C00E77C71 /* ViewController.swift */; }; 13 | E69DFCF7225C6A5C00E77C71 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E69DFCF5225C6A5C00E77C71 /* Main.storyboard */; }; 14 | E69DFCF9225C6A5D00E77C71 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E69DFCF8225C6A5D00E77C71 /* Assets.xcassets */; }; 15 | E69DFCFC225C6A5D00E77C71 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E69DFCFA225C6A5D00E77C71 /* LaunchScreen.storyboard */; }; 16 | E69DFD06225CA6F600E77C71 /* LVThemeKitExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69DFD05225CA6F600E77C71 /* LVThemeKitExtension.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 18A5831482BF1A87D4FE09A8 /* Pods_DayNightDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DayNightDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 5D5C6C4CDC9E0D6537BB3D64 /* Pods-DayNightDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DayNightDemo.debug.xcconfig"; path = "Target Support Files/Pods-DayNightDemo/Pods-DayNightDemo.debug.xcconfig"; sourceTree = ""; }; 22 | 9C558E747E1D4BA36CF77177 /* Pods-DayNightDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DayNightDemo.release.xcconfig"; path = "Target Support Files/Pods-DayNightDemo/Pods-DayNightDemo.release.xcconfig"; sourceTree = ""; }; 23 | E69DFCEE225C6A5C00E77C71 /* DayNightDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DayNightDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | E69DFCF1225C6A5C00E77C71 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 25 | E69DFCF3225C6A5C00E77C71 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 26 | E69DFCF6225C6A5C00E77C71 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | E69DFCF8225C6A5D00E77C71 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | E69DFCFB225C6A5D00E77C71 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 29 | E69DFCFD225C6A5D00E77C71 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | E69DFD05225CA6F600E77C71 /* LVThemeKitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LVThemeKitExtension.swift; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | E69DFCEB225C6A5C00E77C71 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | 6298907890973D1D843456F7 /* Pods_DayNightDemo.framework in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 633EFC3574A8E8FE29D6650D /* Pods */ = { 46 | isa = PBXGroup; 47 | children = ( 48 | 5D5C6C4CDC9E0D6537BB3D64 /* Pods-DayNightDemo.debug.xcconfig */, 49 | 9C558E747E1D4BA36CF77177 /* Pods-DayNightDemo.release.xcconfig */, 50 | ); 51 | path = Pods; 52 | sourceTree = ""; 53 | }; 54 | B4BF9F8004D5F37DB97BF07F /* Frameworks */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 18A5831482BF1A87D4FE09A8 /* Pods_DayNightDemo.framework */, 58 | ); 59 | name = Frameworks; 60 | sourceTree = ""; 61 | }; 62 | E69DFCE5225C6A5C00E77C71 = { 63 | isa = PBXGroup; 64 | children = ( 65 | E69DFCF0225C6A5C00E77C71 /* DayNightDemo */, 66 | E69DFCEF225C6A5C00E77C71 /* Products */, 67 | 633EFC3574A8E8FE29D6650D /* Pods */, 68 | B4BF9F8004D5F37DB97BF07F /* Frameworks */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | E69DFCEF225C6A5C00E77C71 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | E69DFCEE225C6A5C00E77C71 /* DayNightDemo.app */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | E69DFCF0225C6A5C00E77C71 /* DayNightDemo */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | E69DFD05225CA6F600E77C71 /* LVThemeKitExtension.swift */, 84 | E69DFCF1225C6A5C00E77C71 /* AppDelegate.swift */, 85 | E69DFCF3225C6A5C00E77C71 /* ViewController.swift */, 86 | E69DFCF5225C6A5C00E77C71 /* Main.storyboard */, 87 | E69DFCF8225C6A5D00E77C71 /* Assets.xcassets */, 88 | E69DFCFA225C6A5D00E77C71 /* LaunchScreen.storyboard */, 89 | E69DFCFD225C6A5D00E77C71 /* Info.plist */, 90 | ); 91 | path = DayNightDemo; 92 | sourceTree = ""; 93 | }; 94 | /* End PBXGroup section */ 95 | 96 | /* Begin PBXNativeTarget section */ 97 | E69DFCED225C6A5C00E77C71 /* DayNightDemo */ = { 98 | isa = PBXNativeTarget; 99 | buildConfigurationList = E69DFD00225C6A5D00E77C71 /* Build configuration list for PBXNativeTarget "DayNightDemo" */; 100 | buildPhases = ( 101 | EB0A6DD64206EFB2F072E9F6 /* [CP] Check Pods Manifest.lock */, 102 | E69DFCEA225C6A5C00E77C71 /* Sources */, 103 | E69DFCEB225C6A5C00E77C71 /* Frameworks */, 104 | E69DFCEC225C6A5C00E77C71 /* Resources */, 105 | 6091F4E981042B470CDF7949 /* [CP] Embed Pods Frameworks */, 106 | ); 107 | buildRules = ( 108 | ); 109 | dependencies = ( 110 | ); 111 | name = DayNightDemo; 112 | productName = DayNightDemo; 113 | productReference = E69DFCEE225C6A5C00E77C71 /* DayNightDemo.app */; 114 | productType = "com.apple.product-type.application"; 115 | }; 116 | /* End PBXNativeTarget section */ 117 | 118 | /* Begin PBXProject section */ 119 | E69DFCE6225C6A5C00E77C71 /* Project object */ = { 120 | isa = PBXProject; 121 | attributes = { 122 | LastSwiftUpdateCheck = 1020; 123 | LastUpgradeCheck = 1020; 124 | ORGANIZATIONNAME = lvpengwei; 125 | TargetAttributes = { 126 | E69DFCED225C6A5C00E77C71 = { 127 | CreatedOnToolsVersion = 10.2; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = E69DFCE9225C6A5C00E77C71 /* Build configuration list for PBXProject "DayNightDemo" */; 132 | compatibilityVersion = "Xcode 9.3"; 133 | developmentRegion = en; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | Base, 138 | ); 139 | mainGroup = E69DFCE5225C6A5C00E77C71; 140 | productRefGroup = E69DFCEF225C6A5C00E77C71 /* Products */; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | E69DFCED225C6A5C00E77C71 /* DayNightDemo */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXResourcesBuildPhase section */ 150 | E69DFCEC225C6A5C00E77C71 /* Resources */ = { 151 | isa = PBXResourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | E69DFCFC225C6A5D00E77C71 /* LaunchScreen.storyboard in Resources */, 155 | E69DFCF9225C6A5D00E77C71 /* Assets.xcassets in Resources */, 156 | E69DFCF7225C6A5C00E77C71 /* Main.storyboard in Resources */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | /* End PBXResourcesBuildPhase section */ 161 | 162 | /* Begin PBXShellScriptBuildPhase section */ 163 | 6091F4E981042B470CDF7949 /* [CP] Embed Pods Frameworks */ = { 164 | isa = PBXShellScriptBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | ); 168 | inputFileListPaths = ( 169 | ); 170 | inputPaths = ( 171 | "${PODS_ROOT}/Target Support Files/Pods-DayNightDemo/Pods-DayNightDemo-frameworks.sh", 172 | "${BUILT_PRODUCTS_DIR}/LVThemeKit/LVThemeKit.framework", 173 | ); 174 | name = "[CP] Embed Pods Frameworks"; 175 | outputFileListPaths = ( 176 | ); 177 | outputPaths = ( 178 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LVThemeKit.framework", 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | shellPath = /bin/sh; 182 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DayNightDemo/Pods-DayNightDemo-frameworks.sh\"\n"; 183 | showEnvVarsInLog = 0; 184 | }; 185 | EB0A6DD64206EFB2F072E9F6 /* [CP] Check Pods Manifest.lock */ = { 186 | isa = PBXShellScriptBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | ); 190 | inputFileListPaths = ( 191 | ); 192 | inputPaths = ( 193 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 194 | "${PODS_ROOT}/Manifest.lock", 195 | ); 196 | name = "[CP] Check Pods Manifest.lock"; 197 | outputFileListPaths = ( 198 | ); 199 | outputPaths = ( 200 | "$(DERIVED_FILE_DIR)/Pods-DayNightDemo-checkManifestLockResult.txt", 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | shellPath = /bin/sh; 204 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 205 | showEnvVarsInLog = 0; 206 | }; 207 | /* End PBXShellScriptBuildPhase section */ 208 | 209 | /* Begin PBXSourcesBuildPhase section */ 210 | E69DFCEA225C6A5C00E77C71 /* Sources */ = { 211 | isa = PBXSourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | E69DFD06225CA6F600E77C71 /* LVThemeKitExtension.swift in Sources */, 215 | E69DFCF4225C6A5C00E77C71 /* ViewController.swift in Sources */, 216 | E69DFCF2225C6A5C00E77C71 /* AppDelegate.swift in Sources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXSourcesBuildPhase section */ 221 | 222 | /* Begin PBXVariantGroup section */ 223 | E69DFCF5225C6A5C00E77C71 /* Main.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | E69DFCF6225C6A5C00E77C71 /* Base */, 227 | ); 228 | name = Main.storyboard; 229 | sourceTree = ""; 230 | }; 231 | E69DFCFA225C6A5D00E77C71 /* LaunchScreen.storyboard */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | E69DFCFB225C6A5D00E77C71 /* Base */, 235 | ); 236 | name = LaunchScreen.storyboard; 237 | sourceTree = ""; 238 | }; 239 | /* End PBXVariantGroup section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | E69DFCFE225C6A5D00E77C71 /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 249 | CLANG_CXX_LIBRARY = "libc++"; 250 | CLANG_ENABLE_MODULES = YES; 251 | CLANG_ENABLE_OBJC_ARC = YES; 252 | CLANG_ENABLE_OBJC_WEAK = YES; 253 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 254 | CLANG_WARN_BOOL_CONVERSION = YES; 255 | CLANG_WARN_COMMA = YES; 256 | CLANG_WARN_CONSTANT_CONVERSION = YES; 257 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 259 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 260 | CLANG_WARN_EMPTY_BODY = YES; 261 | CLANG_WARN_ENUM_CONVERSION = YES; 262 | CLANG_WARN_INFINITE_RECURSION = YES; 263 | CLANG_WARN_INT_CONVERSION = YES; 264 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 266 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 267 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 268 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 269 | CLANG_WARN_STRICT_PROTOTYPES = YES; 270 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 271 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 272 | CLANG_WARN_UNREACHABLE_CODE = YES; 273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 274 | CODE_SIGN_IDENTITY = "iPhone Developer"; 275 | COPY_PHASE_STRIP = NO; 276 | DEBUG_INFORMATION_FORMAT = dwarf; 277 | ENABLE_STRICT_OBJC_MSGSEND = YES; 278 | ENABLE_TESTABILITY = YES; 279 | GCC_C_LANGUAGE_STANDARD = gnu11; 280 | GCC_DYNAMIC_NO_PIC = NO; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_OPTIMIZATION_LEVEL = 0; 283 | GCC_PREPROCESSOR_DEFINITIONS = ( 284 | "DEBUG=1", 285 | "$(inherited)", 286 | ); 287 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 288 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 289 | GCC_WARN_UNDECLARED_SELECTOR = YES; 290 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 291 | GCC_WARN_UNUSED_FUNCTION = YES; 292 | GCC_WARN_UNUSED_VARIABLE = YES; 293 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 294 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 295 | MTL_FAST_MATH = YES; 296 | ONLY_ACTIVE_ARCH = YES; 297 | SDKROOT = iphoneos; 298 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 299 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 300 | }; 301 | name = Debug; 302 | }; 303 | E69DFCFF225C6A5D00E77C71 /* Release */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | CLANG_ANALYZER_NONNULL = YES; 308 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 309 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 310 | CLANG_CXX_LIBRARY = "libc++"; 311 | CLANG_ENABLE_MODULES = YES; 312 | CLANG_ENABLE_OBJC_ARC = YES; 313 | CLANG_ENABLE_OBJC_WEAK = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INFINITE_RECURSION = YES; 324 | CLANG_WARN_INT_CONVERSION = YES; 325 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 327 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 328 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 329 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 330 | CLANG_WARN_STRICT_PROTOTYPES = YES; 331 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 332 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 333 | CLANG_WARN_UNREACHABLE_CODE = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | CODE_SIGN_IDENTITY = "iPhone Developer"; 336 | COPY_PHASE_STRIP = NO; 337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 338 | ENABLE_NS_ASSERTIONS = NO; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | GCC_C_LANGUAGE_STANDARD = gnu11; 341 | GCC_NO_COMMON_BLOCKS = YES; 342 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 343 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 344 | GCC_WARN_UNDECLARED_SELECTOR = YES; 345 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 346 | GCC_WARN_UNUSED_FUNCTION = YES; 347 | GCC_WARN_UNUSED_VARIABLE = YES; 348 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 349 | MTL_ENABLE_DEBUG_INFO = NO; 350 | MTL_FAST_MATH = YES; 351 | SDKROOT = iphoneos; 352 | SWIFT_COMPILATION_MODE = wholemodule; 353 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 354 | VALIDATE_PRODUCT = YES; 355 | }; 356 | name = Release; 357 | }; 358 | E69DFD01225C6A5D00E77C71 /* Debug */ = { 359 | isa = XCBuildConfiguration; 360 | baseConfigurationReference = 5D5C6C4CDC9E0D6537BB3D64 /* Pods-DayNightDemo.debug.xcconfig */; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | CODE_SIGN_STYLE = Automatic; 364 | DEVELOPMENT_TEAM = 3A75DREAB8; 365 | INFOPLIST_FILE = DayNightDemo/Info.plist; 366 | LD_RUNPATH_SEARCH_PATHS = ( 367 | "$(inherited)", 368 | "@executable_path/Frameworks", 369 | ); 370 | PRODUCT_BUNDLE_IDENTIFIER = com.lvpengwei.DayNightDemo; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | SWIFT_VERSION = 5.0; 373 | TARGETED_DEVICE_FAMILY = "1,2"; 374 | }; 375 | name = Debug; 376 | }; 377 | E69DFD02225C6A5D00E77C71 /* Release */ = { 378 | isa = XCBuildConfiguration; 379 | baseConfigurationReference = 9C558E747E1D4BA36CF77177 /* Pods-DayNightDemo.release.xcconfig */; 380 | buildSettings = { 381 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 382 | CODE_SIGN_STYLE = Automatic; 383 | DEVELOPMENT_TEAM = 3A75DREAB8; 384 | INFOPLIST_FILE = DayNightDemo/Info.plist; 385 | LD_RUNPATH_SEARCH_PATHS = ( 386 | "$(inherited)", 387 | "@executable_path/Frameworks", 388 | ); 389 | PRODUCT_BUNDLE_IDENTIFIER = com.lvpengwei.DayNightDemo; 390 | PRODUCT_NAME = "$(TARGET_NAME)"; 391 | SWIFT_VERSION = 5.0; 392 | TARGETED_DEVICE_FAMILY = "1,2"; 393 | }; 394 | name = Release; 395 | }; 396 | /* End XCBuildConfiguration section */ 397 | 398 | /* Begin XCConfigurationList section */ 399 | E69DFCE9225C6A5C00E77C71 /* Build configuration list for PBXProject "DayNightDemo" */ = { 400 | isa = XCConfigurationList; 401 | buildConfigurations = ( 402 | E69DFCFE225C6A5D00E77C71 /* Debug */, 403 | E69DFCFF225C6A5D00E77C71 /* Release */, 404 | ); 405 | defaultConfigurationIsVisible = 0; 406 | defaultConfigurationName = Release; 407 | }; 408 | E69DFD00225C6A5D00E77C71 /* Build configuration list for PBXNativeTarget "DayNightDemo" */ = { 409 | isa = XCConfigurationList; 410 | buildConfigurations = ( 411 | E69DFD01225C6A5D00E77C71 /* Debug */, 412 | E69DFD02225C6A5D00E77C71 /* Release */, 413 | ); 414 | defaultConfigurationIsVisible = 0; 415 | defaultConfigurationName = Release; 416 | }; 417 | /* End XCConfigurationList section */ 418 | }; 419 | rootObject = E69DFCE6225C6A5C00E77C71 /* Project object */; 420 | } 421 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DayNightDemo 4 | // 5 | // Created by lvpengwei on 2019/4/9. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | DayNightManager.config() 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_rijian.imageset/24_rijian@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvpengwei/LVThemeKit/d7f6d4397e16e13b74f27cf69d134ca4861a6f84/Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_rijian.imageset/24_rijian@2x.png -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_rijian.imageset/24_rijian@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvpengwei/LVThemeKit/d7f6d4397e16e13b74f27cf69d134ca4861a6f84/Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_rijian.imageset/24_rijian@3x.png -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_rijian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "24_rijian@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "24_rijian@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_yejian.imageset/24_yejian@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvpengwei/LVThemeKit/d7f6d4397e16e13b74f27cf69d134ca4861a6f84/Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_yejian.imageset/24_yejian@2x.png -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_yejian.imageset/24_yejian@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvpengwei/LVThemeKit/d7f6d4397e16e13b74f27cf69d134ca4861a6f84/Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_yejian.imageset/24_yejian@3x.png -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Assets.xcassets/24_yejian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "24_yejian@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "24_yejian@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 47 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/LVThemeKitExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeKitExtension.swift 3 | // DayNightDemo 4 | // 5 | // Created by lvpengwei on 2019/4/9. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import LVThemeKit 11 | 12 | 13 | extension Notification.Name { 14 | static let dayNightChanged = Notification.Name("day_night_changed") 15 | } 16 | let DAY_NIGHT_FONT = "font" 17 | let DAY_NIGHT_BACKGROUND = "background" 18 | let DAY_NIGHT_BACKGROUNDLIGHT = "backgroundLight" 19 | let DAY_NIGHT_HIGHLIGHT = "highlight" 20 | class DayNightManager { 21 | fileprivate static let sharedInstance = DayNightManager() 22 | public class var shared: DayNightManager { 23 | get { 24 | return sharedInstance 25 | } 26 | } 27 | var type = DayNightType.day { 28 | didSet { 29 | NotificationCenter.default.post(name: .dayNightChanged, object: nil) 30 | } 31 | } 32 | var night: Bool { 33 | return type == .night 34 | } 35 | enum DayNightType: Int { 36 | case day = 0 37 | case night 38 | } 39 | private var map: [[String: String]] 40 | init() { 41 | map = [[DAY_NIGHT_FONT: "#262626FF", DAY_NIGHT_BACKGROUND: "#F6F6F6FF", DAY_NIGHT_BACKGROUNDLIGHT: "#FFFFFFFF", DAY_NIGHT_HIGHLIGHT: "#ED424BFF"], 42 | [DAY_NIGHT_FONT: "#CCCCCCFF", DAY_NIGHT_BACKGROUND: "#1A1A1AFF", DAY_NIGHT_BACKGROUNDLIGHT: "#262626FF", DAY_NIGHT_HIGHLIGHT: "#3D5CB8FF"]] 43 | } 44 | func color(with key: String) -> UIColor? { 45 | guard let hex = map[type.rawValue][key] else { return nil } 46 | return UIColor(hex: hex) 47 | } 48 | func image() -> UIImage? { 49 | return !night ? UIImage(named: "24_rijian") : UIImage(named: "24_yejian") 50 | } 51 | class func change() { 52 | shared.type = shared.night ? .day : .night 53 | } 54 | class func config() { 55 | let conf = LVThemeKitConfig() 56 | conf.generators = [DayNightObserver.self] 57 | LVThemeKit.config = conf 58 | } 59 | } 60 | 61 | extension UIColor { 62 | public convenience init?(hex: String) { 63 | let r, g, b, a: CGFloat 64 | 65 | if hex.hasPrefix("#") { 66 | let start = hex.index(hex.startIndex, offsetBy: 1) 67 | let hexColor = String(hex[start...]) 68 | 69 | if hexColor.count == 8 { 70 | let scanner = Scanner(string: hexColor) 71 | var hexNumber: UInt64 = 0 72 | 73 | if scanner.scanHexInt64(&hexNumber) { 74 | r = CGFloat((hexNumber & 0xff000000) >> 24) / 255 75 | g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255 76 | b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255 77 | a = CGFloat(hexNumber & 0x000000ff) / 255 78 | 79 | self.init(red: r, green: g, blue: b, alpha: a) 80 | return 81 | } 82 | } 83 | } 84 | 85 | return nil 86 | } 87 | } 88 | 89 | extension LVThemeString { 90 | class var dayNightString: LVThemeString { 91 | return LVThemeString.instance({ () -> NSString in 92 | return DayNightManager.shared.night ? "黑夜" : "白天" 93 | }) 94 | } 95 | } 96 | 97 | extension LVThemeColor { 98 | class var font: LVThemeColor { 99 | return LVThemeColor.instance({ () -> UIColor? in 100 | return DayNightManager.shared.color(with: DAY_NIGHT_FONT) 101 | }) 102 | } 103 | class var background: LVThemeColor { 104 | return LVThemeColor.instance({ () -> UIColor? in 105 | return DayNightManager.shared.color(with: DAY_NIGHT_BACKGROUND) 106 | }) 107 | } 108 | class var backgroundLight: LVThemeColor { 109 | return LVThemeColor.instance({ () -> UIColor? in 110 | return DayNightManager.shared.color(with: DAY_NIGHT_BACKGROUNDLIGHT) 111 | }) 112 | } 113 | class var highlight: LVThemeColor { 114 | return LVThemeColor.instance({ () -> UIColor? in 115 | return DayNightManager.shared.color(with: DAY_NIGHT_HIGHLIGHT) 116 | }) 117 | } 118 | } 119 | 120 | extension LVThemeImage { 121 | class var dayNightImage: LVThemeImage { 122 | return LVThemeImage.instance({ () -> UIImage? in 123 | return DayNightManager.shared.image() 124 | }) 125 | } 126 | } 127 | 128 | class DayNightObserver: NSObject, LVThemeKitObserverGenerator { 129 | var observer: Any? 130 | func lvThemeKitObserverGeneratorCompletion(_ completion: @escaping LVThemeKitCompletion) { 131 | observer = NotificationCenter.default.addObserver(forName: .dayNightChanged, object: nil, queue: OperationQueue.main) { (_) in 132 | completion() 133 | } 134 | } 135 | deinit { 136 | if let observer = observer { 137 | NotificationCenter.default.removeObserver(observer) 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/DayNightDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DayNightDemo 4 | // 5 | // Created by lvpengwei on 2019/4/9. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import LVThemeKit 11 | 12 | class ViewController: UIViewController { 13 | @IBOutlet weak var openNewBtn: UIButton! 14 | @IBOutlet weak var switchBtn: UIButton! 15 | @IBOutlet weak var label: UILabel! 16 | @IBOutlet weak var testView: UIView! 17 | @IBOutlet weak var imageView: UIImageView! 18 | @IBOutlet weak var slider: UISlider! 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | switchBtn.viewTK.theme.tintColor = LVThemeColor.highlight 22 | switchBtn.buttonTK.theme.setImage(LVThemeImage.dayNightImage, for: .normal) 23 | switchBtn.buttonTK.theme.setTitle(LVThemeString.dayNightString, for: .normal) 24 | label.labelTK.theme.textColor = LVThemeColor.font 25 | label.viewTK.theme.backgroundColor = LVThemeColor.background 26 | testView.layer.borderWidth = 1 27 | testView.layer.layerTK.theme.borderColor = LVThemeColor.highlight 28 | imageView.imageTK.theme.image = LVThemeImage.dayNightImage 29 | slider.sliderTK.theme.minimumTrackTintColor = LVThemeColor.background 30 | slider.sliderTK.theme.maximumTrackTintColor = LVThemeColor.highlight 31 | } 32 | @IBAction func switchAction(_ sender: Any) { 33 | DayNightManager.change() 34 | } 35 | @IBAction func openAction(_ sender: Any) { 36 | let sb = UIStoryboard(name: "Main", bundle: nil) 37 | guard let vc = [sb .instantiateViewController(withIdentifier: "ViewController")].first else { return } 38 | navigationController?.pushViewController(vc, animated: true) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :ios, '8.0' 3 | 4 | inhibit_all_warnings! 5 | 6 | target 'DayNightDemo' do 7 | use_frameworks! 8 | 9 | pod 'LVThemeKit/ObjC', :path => '../../' 10 | end 11 | -------------------------------------------------------------------------------- /Examples/DayNightDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - LVThemeKit/ObjC (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - LVThemeKit/ObjC (from `../../`) 6 | 7 | EXTERNAL SOURCES: 8 | LVThemeKit: 9 | :path: "../../" 10 | 11 | SPEC CHECKSUMS: 12 | LVThemeKit: 90969fa20d54cf9bae01e953588bbb0337bbaaf6 13 | 14 | PODFILE CHECKSUM: 4bd59fa0882aa67ad236cc533c6cec3e77681c0a 15 | 16 | COCOAPODS: 1.6.1 17 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CD1F2FF3D4552455E9AD6965 /* Pods_DayNightDemoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15C3CA816A8D85BAF2B7D3F9 /* Pods_DayNightDemoSwift.framework */; }; 11 | E64135DE225D8B38008D1497 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E64135DD225D8B38008D1497 /* AppDelegate.swift */; }; 12 | E64135E0225D8B38008D1497 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E64135DF225D8B38008D1497 /* ViewController.swift */; }; 13 | E64135E3225D8B38008D1497 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E64135E1225D8B38008D1497 /* Main.storyboard */; }; 14 | E64135E5225D8B39008D1497 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E64135E4225D8B39008D1497 /* Assets.xcassets */; }; 15 | E64135E8225D8B39008D1497 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E64135E6225D8B39008D1497 /* LaunchScreen.storyboard */; }; 16 | E64135F4225DAB10008D1497 /* LVThemeKitExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E64135F3225DAB10008D1497 /* LVThemeKitExtension.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 15C3CA816A8D85BAF2B7D3F9 /* Pods_DayNightDemoSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DayNightDemoSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 56DEEB0F3B5CB483104484DD /* Pods-DayNightDemoSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DayNightDemoSwift.release.xcconfig"; path = "Target Support Files/Pods-DayNightDemoSwift/Pods-DayNightDemoSwift.release.xcconfig"; sourceTree = ""; }; 22 | 79CEF9974D2429158A3A5243 /* Pods-DayNightDemoSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DayNightDemoSwift.debug.xcconfig"; path = "Target Support Files/Pods-DayNightDemoSwift/Pods-DayNightDemoSwift.debug.xcconfig"; sourceTree = ""; }; 23 | E64135DA225D8B38008D1497 /* DayNightDemoSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DayNightDemoSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | E64135DD225D8B38008D1497 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 25 | E64135DF225D8B38008D1497 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 26 | E64135E2225D8B38008D1497 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | E64135E4225D8B39008D1497 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | E64135E7225D8B39008D1497 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 29 | E64135E9225D8B39008D1497 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | E64135F3225DAB10008D1497 /* LVThemeKitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LVThemeKitExtension.swift; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | E64135D7225D8B38008D1497 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | CD1F2FF3D4552455E9AD6965 /* Pods_DayNightDemoSwift.framework in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 18F56C128170EF2C79A5E871 /* Frameworks */ = { 46 | isa = PBXGroup; 47 | children = ( 48 | 15C3CA816A8D85BAF2B7D3F9 /* Pods_DayNightDemoSwift.framework */, 49 | ); 50 | name = Frameworks; 51 | sourceTree = ""; 52 | }; 53 | E64135D1225D8B38008D1497 = { 54 | isa = PBXGroup; 55 | children = ( 56 | E64135DC225D8B38008D1497 /* DayNightDemoSwift */, 57 | E64135DB225D8B38008D1497 /* Products */, 58 | F0720E80FB2222DE9D8C15BD /* Pods */, 59 | 18F56C128170EF2C79A5E871 /* Frameworks */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | E64135DB225D8B38008D1497 /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | E64135DA225D8B38008D1497 /* DayNightDemoSwift.app */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | E64135DC225D8B38008D1497 /* DayNightDemoSwift */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | E64135F3225DAB10008D1497 /* LVThemeKitExtension.swift */, 75 | E64135DD225D8B38008D1497 /* AppDelegate.swift */, 76 | E64135DF225D8B38008D1497 /* ViewController.swift */, 77 | E64135E1225D8B38008D1497 /* Main.storyboard */, 78 | E64135E4225D8B39008D1497 /* Assets.xcassets */, 79 | E64135E6225D8B39008D1497 /* LaunchScreen.storyboard */, 80 | E64135E9225D8B39008D1497 /* Info.plist */, 81 | ); 82 | path = DayNightDemoSwift; 83 | sourceTree = ""; 84 | }; 85 | F0720E80FB2222DE9D8C15BD /* Pods */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 79CEF9974D2429158A3A5243 /* Pods-DayNightDemoSwift.debug.xcconfig */, 89 | 56DEEB0F3B5CB483104484DD /* Pods-DayNightDemoSwift.release.xcconfig */, 90 | ); 91 | path = Pods; 92 | sourceTree = ""; 93 | }; 94 | /* End PBXGroup section */ 95 | 96 | /* Begin PBXNativeTarget section */ 97 | E64135D9225D8B38008D1497 /* DayNightDemoSwift */ = { 98 | isa = PBXNativeTarget; 99 | buildConfigurationList = E64135EC225D8B39008D1497 /* Build configuration list for PBXNativeTarget "DayNightDemoSwift" */; 100 | buildPhases = ( 101 | 6AFEFA36F386188400D271B7 /* [CP] Check Pods Manifest.lock */, 102 | E64135D6225D8B38008D1497 /* Sources */, 103 | E64135D7225D8B38008D1497 /* Frameworks */, 104 | E64135D8225D8B38008D1497 /* Resources */, 105 | CE207017461B0F48DFA47A3F /* [CP] Embed Pods Frameworks */, 106 | ); 107 | buildRules = ( 108 | ); 109 | dependencies = ( 110 | ); 111 | name = DayNightDemoSwift; 112 | productName = DayNightDemoSwift; 113 | productReference = E64135DA225D8B38008D1497 /* DayNightDemoSwift.app */; 114 | productType = "com.apple.product-type.application"; 115 | }; 116 | /* End PBXNativeTarget section */ 117 | 118 | /* Begin PBXProject section */ 119 | E64135D2225D8B38008D1497 /* Project object */ = { 120 | isa = PBXProject; 121 | attributes = { 122 | LastSwiftUpdateCheck = 1020; 123 | LastUpgradeCheck = 1020; 124 | ORGANIZATIONNAME = lvpengwei; 125 | TargetAttributes = { 126 | E64135D9225D8B38008D1497 = { 127 | CreatedOnToolsVersion = 10.2; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = E64135D5225D8B38008D1497 /* Build configuration list for PBXProject "DayNightDemoSwift" */; 132 | compatibilityVersion = "Xcode 9.3"; 133 | developmentRegion = en; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | Base, 138 | ); 139 | mainGroup = E64135D1225D8B38008D1497; 140 | productRefGroup = E64135DB225D8B38008D1497 /* Products */; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | E64135D9225D8B38008D1497 /* DayNightDemoSwift */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXResourcesBuildPhase section */ 150 | E64135D8225D8B38008D1497 /* Resources */ = { 151 | isa = PBXResourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | E64135E8225D8B39008D1497 /* LaunchScreen.storyboard in Resources */, 155 | E64135E5225D8B39008D1497 /* Assets.xcassets in Resources */, 156 | E64135E3225D8B38008D1497 /* Main.storyboard in Resources */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | /* End PBXResourcesBuildPhase section */ 161 | 162 | /* Begin PBXShellScriptBuildPhase section */ 163 | 6AFEFA36F386188400D271B7 /* [CP] Check Pods Manifest.lock */ = { 164 | isa = PBXShellScriptBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | ); 168 | inputFileListPaths = ( 169 | ); 170 | inputPaths = ( 171 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 172 | "${PODS_ROOT}/Manifest.lock", 173 | ); 174 | name = "[CP] Check Pods Manifest.lock"; 175 | outputFileListPaths = ( 176 | ); 177 | outputPaths = ( 178 | "$(DERIVED_FILE_DIR)/Pods-DayNightDemoSwift-checkManifestLockResult.txt", 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | shellPath = /bin/sh; 182 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 183 | showEnvVarsInLog = 0; 184 | }; 185 | CE207017461B0F48DFA47A3F /* [CP] Embed Pods Frameworks */ = { 186 | isa = PBXShellScriptBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | ); 190 | inputFileListPaths = ( 191 | ); 192 | inputPaths = ( 193 | "${PODS_ROOT}/Target Support Files/Pods-DayNightDemoSwift/Pods-DayNightDemoSwift-frameworks.sh", 194 | "${BUILT_PRODUCTS_DIR}/LVThemeKit/LVThemeKit.framework", 195 | ); 196 | name = "[CP] Embed Pods Frameworks"; 197 | outputFileListPaths = ( 198 | ); 199 | outputPaths = ( 200 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LVThemeKit.framework", 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | shellPath = /bin/sh; 204 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DayNightDemoSwift/Pods-DayNightDemoSwift-frameworks.sh\"\n"; 205 | showEnvVarsInLog = 0; 206 | }; 207 | /* End PBXShellScriptBuildPhase section */ 208 | 209 | /* Begin PBXSourcesBuildPhase section */ 210 | E64135D6225D8B38008D1497 /* Sources */ = { 211 | isa = PBXSourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | E64135F4225DAB10008D1497 /* LVThemeKitExtension.swift in Sources */, 215 | E64135E0225D8B38008D1497 /* ViewController.swift in Sources */, 216 | E64135DE225D8B38008D1497 /* AppDelegate.swift in Sources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXSourcesBuildPhase section */ 221 | 222 | /* Begin PBXVariantGroup section */ 223 | E64135E1225D8B38008D1497 /* Main.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | E64135E2225D8B38008D1497 /* Base */, 227 | ); 228 | name = Main.storyboard; 229 | sourceTree = ""; 230 | }; 231 | E64135E6225D8B39008D1497 /* LaunchScreen.storyboard */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | E64135E7225D8B39008D1497 /* Base */, 235 | ); 236 | name = LaunchScreen.storyboard; 237 | sourceTree = ""; 238 | }; 239 | /* End PBXVariantGroup section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | E64135EA225D8B39008D1497 /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 249 | CLANG_CXX_LIBRARY = "libc++"; 250 | CLANG_ENABLE_MODULES = YES; 251 | CLANG_ENABLE_OBJC_ARC = YES; 252 | CLANG_ENABLE_OBJC_WEAK = YES; 253 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 254 | CLANG_WARN_BOOL_CONVERSION = YES; 255 | CLANG_WARN_COMMA = YES; 256 | CLANG_WARN_CONSTANT_CONVERSION = YES; 257 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 259 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 260 | CLANG_WARN_EMPTY_BODY = YES; 261 | CLANG_WARN_ENUM_CONVERSION = YES; 262 | CLANG_WARN_INFINITE_RECURSION = YES; 263 | CLANG_WARN_INT_CONVERSION = YES; 264 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 266 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 267 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 268 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 269 | CLANG_WARN_STRICT_PROTOTYPES = YES; 270 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 271 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 272 | CLANG_WARN_UNREACHABLE_CODE = YES; 273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 274 | CODE_SIGN_IDENTITY = "iPhone Developer"; 275 | COPY_PHASE_STRIP = NO; 276 | DEBUG_INFORMATION_FORMAT = dwarf; 277 | ENABLE_STRICT_OBJC_MSGSEND = YES; 278 | ENABLE_TESTABILITY = YES; 279 | GCC_C_LANGUAGE_STANDARD = gnu11; 280 | GCC_DYNAMIC_NO_PIC = NO; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_OPTIMIZATION_LEVEL = 0; 283 | GCC_PREPROCESSOR_DEFINITIONS = ( 284 | "DEBUG=1", 285 | "$(inherited)", 286 | ); 287 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 288 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 289 | GCC_WARN_UNDECLARED_SELECTOR = YES; 290 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 291 | GCC_WARN_UNUSED_FUNCTION = YES; 292 | GCC_WARN_UNUSED_VARIABLE = YES; 293 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 294 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 295 | MTL_FAST_MATH = YES; 296 | ONLY_ACTIVE_ARCH = YES; 297 | SDKROOT = iphoneos; 298 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 299 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 300 | }; 301 | name = Debug; 302 | }; 303 | E64135EB225D8B39008D1497 /* Release */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | CLANG_ANALYZER_NONNULL = YES; 308 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 309 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 310 | CLANG_CXX_LIBRARY = "libc++"; 311 | CLANG_ENABLE_MODULES = YES; 312 | CLANG_ENABLE_OBJC_ARC = YES; 313 | CLANG_ENABLE_OBJC_WEAK = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INFINITE_RECURSION = YES; 324 | CLANG_WARN_INT_CONVERSION = YES; 325 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 327 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 328 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 329 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 330 | CLANG_WARN_STRICT_PROTOTYPES = YES; 331 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 332 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 333 | CLANG_WARN_UNREACHABLE_CODE = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | CODE_SIGN_IDENTITY = "iPhone Developer"; 336 | COPY_PHASE_STRIP = NO; 337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 338 | ENABLE_NS_ASSERTIONS = NO; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | GCC_C_LANGUAGE_STANDARD = gnu11; 341 | GCC_NO_COMMON_BLOCKS = YES; 342 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 343 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 344 | GCC_WARN_UNDECLARED_SELECTOR = YES; 345 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 346 | GCC_WARN_UNUSED_FUNCTION = YES; 347 | GCC_WARN_UNUSED_VARIABLE = YES; 348 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 349 | MTL_ENABLE_DEBUG_INFO = NO; 350 | MTL_FAST_MATH = YES; 351 | SDKROOT = iphoneos; 352 | SWIFT_COMPILATION_MODE = wholemodule; 353 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 354 | VALIDATE_PRODUCT = YES; 355 | }; 356 | name = Release; 357 | }; 358 | E64135ED225D8B39008D1497 /* Debug */ = { 359 | isa = XCBuildConfiguration; 360 | baseConfigurationReference = 79CEF9974D2429158A3A5243 /* Pods-DayNightDemoSwift.debug.xcconfig */; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | CODE_SIGN_STYLE = Automatic; 364 | DEVELOPMENT_TEAM = 3A75DREAB8; 365 | INFOPLIST_FILE = DayNightDemoSwift/Info.plist; 366 | LD_RUNPATH_SEARCH_PATHS = ( 367 | "$(inherited)", 368 | "@executable_path/Frameworks", 369 | ); 370 | PRODUCT_BUNDLE_IDENTIFIER = com.lvpengwei.DayNightDemoSwift; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | SWIFT_VERSION = 5.0; 373 | TARGETED_DEVICE_FAMILY = "1,2"; 374 | }; 375 | name = Debug; 376 | }; 377 | E64135EE225D8B39008D1497 /* Release */ = { 378 | isa = XCBuildConfiguration; 379 | baseConfigurationReference = 56DEEB0F3B5CB483104484DD /* Pods-DayNightDemoSwift.release.xcconfig */; 380 | buildSettings = { 381 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 382 | CODE_SIGN_STYLE = Automatic; 383 | DEVELOPMENT_TEAM = 3A75DREAB8; 384 | INFOPLIST_FILE = DayNightDemoSwift/Info.plist; 385 | LD_RUNPATH_SEARCH_PATHS = ( 386 | "$(inherited)", 387 | "@executable_path/Frameworks", 388 | ); 389 | PRODUCT_BUNDLE_IDENTIFIER = com.lvpengwei.DayNightDemoSwift; 390 | PRODUCT_NAME = "$(TARGET_NAME)"; 391 | SWIFT_VERSION = 5.0; 392 | TARGETED_DEVICE_FAMILY = "1,2"; 393 | }; 394 | name = Release; 395 | }; 396 | /* End XCBuildConfiguration section */ 397 | 398 | /* Begin XCConfigurationList section */ 399 | E64135D5225D8B38008D1497 /* Build configuration list for PBXProject "DayNightDemoSwift" */ = { 400 | isa = XCConfigurationList; 401 | buildConfigurations = ( 402 | E64135EA225D8B39008D1497 /* Debug */, 403 | E64135EB225D8B39008D1497 /* Release */, 404 | ); 405 | defaultConfigurationIsVisible = 0; 406 | defaultConfigurationName = Release; 407 | }; 408 | E64135EC225D8B39008D1497 /* Build configuration list for PBXNativeTarget "DayNightDemoSwift" */ = { 409 | isa = XCConfigurationList; 410 | buildConfigurations = ( 411 | E64135ED225D8B39008D1497 /* Debug */, 412 | E64135EE225D8B39008D1497 /* Release */, 413 | ); 414 | defaultConfigurationIsVisible = 0; 415 | defaultConfigurationName = Release; 416 | }; 417 | /* End XCConfigurationList section */ 418 | }; 419 | rootObject = E64135D2225D8B38008D1497 /* Project object */; 420 | } 421 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DayNightDemoSwift 4 | // 5 | // Created by lvpengwei on 2019/4/10. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | DayNightManager.config() 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_rijian.imageset/24_rijian@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvpengwei/LVThemeKit/d7f6d4397e16e13b74f27cf69d134ca4861a6f84/Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_rijian.imageset/24_rijian@2x.png -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_rijian.imageset/24_rijian@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvpengwei/LVThemeKit/d7f6d4397e16e13b74f27cf69d134ca4861a6f84/Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_rijian.imageset/24_rijian@3x.png -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_rijian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "24_rijian@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "24_rijian@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_yejian.imageset/24_yejian@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvpengwei/LVThemeKit/d7f6d4397e16e13b74f27cf69d134ca4861a6f84/Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_yejian.imageset/24_yejian@2x.png -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_yejian.imageset/24_yejian@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvpengwei/LVThemeKit/d7f6d4397e16e13b74f27cf69d134ca4861a6f84/Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_yejian.imageset/24_yejian@3x.png -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/24_yejian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "24_yejian@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "24_yejian@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/LVThemeKitExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeKitExtension.swift 3 | // DayNightDemoSwift 4 | // 5 | // Created by lvpengwei on 2019/4/10. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import LVThemeKit 11 | 12 | extension Notification.Name { 13 | static let dayNightChanged = Notification.Name("day_night_changed") 14 | } 15 | let DAY_NIGHT_FONT = "font" 16 | let DAY_NIGHT_BACKGROUND = "background" 17 | let DAY_NIGHT_BACKGROUNDLIGHT = "backgroundLight" 18 | let DAY_NIGHT_HIGHLIGHT = "highlight" 19 | class DayNightManager { 20 | fileprivate static let sharedInstance = DayNightManager() 21 | public class var shared: DayNightManager { 22 | get { 23 | return sharedInstance 24 | } 25 | } 26 | var type = DayNightType.day { 27 | didSet { 28 | NotificationCenter.default.post(name: .dayNightChanged, object: nil) 29 | } 30 | } 31 | var night: Bool { 32 | return type == .night 33 | } 34 | enum DayNightType: Int { 35 | case day = 0 36 | case night 37 | } 38 | private var map: [[String: String]] 39 | init() { 40 | map = [[DAY_NIGHT_FONT: "#262626FF", DAY_NIGHT_BACKGROUND: "#F6F6F6FF", DAY_NIGHT_BACKGROUNDLIGHT: "#FFFFFFFF", DAY_NIGHT_HIGHLIGHT: "#ED424BFF"], 41 | [DAY_NIGHT_FONT: "#CCCCCCFF", DAY_NIGHT_BACKGROUND: "#1A1A1AFF", DAY_NIGHT_BACKGROUNDLIGHT: "#262626FF", DAY_NIGHT_HIGHLIGHT: "#3D5CB8FF"]] 42 | } 43 | func color(with key: String) -> UIColor? { 44 | guard let hex = map[type.rawValue][key] else { return nil } 45 | return UIColor(hex: hex) 46 | } 47 | class func change() { 48 | shared.type = shared.night ? .day : .night 49 | } 50 | class func config() { 51 | let conf = LVThemeKitConfig() 52 | conf.generators = [DayNightObserver.self] 53 | LVThemeKitConfig.config = conf 54 | } 55 | } 56 | 57 | extension UIColor { 58 | public convenience init?(hex: String) { 59 | let r, g, b, a: CGFloat 60 | 61 | if hex.hasPrefix("#") { 62 | let start = hex.index(hex.startIndex, offsetBy: 1) 63 | let hexColor = String(hex[start...]) 64 | 65 | if hexColor.count == 8 { 66 | let scanner = Scanner(string: hexColor) 67 | var hexNumber: UInt64 = 0 68 | 69 | if scanner.scanHexInt64(&hexNumber) { 70 | r = CGFloat((hexNumber & 0xff000000) >> 24) / 255 71 | g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255 72 | b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255 73 | a = CGFloat(hexNumber & 0x000000ff) / 255 74 | 75 | self.init(red: r, green: g, blue: b, alpha: a) 76 | return 77 | } 78 | } 79 | } 80 | 81 | return nil 82 | } 83 | } 84 | 85 | extension LVThemeColor { 86 | class var background: LVThemeColor { 87 | return LVThemeColor.init(with: { () -> NSObject? in 88 | return DayNightManager.shared.color(with: DAY_NIGHT_BACKGROUND) 89 | }) 90 | } 91 | class var font: LVThemeColor { 92 | return LVThemeColor.init(with: { () -> NSObject? in 93 | return DayNightManager.shared.color(with: DAY_NIGHT_FONT) 94 | }) 95 | } 96 | class var backgroundLight: LVThemeColor { 97 | return LVThemeColor.init(with: { () -> NSObject? in 98 | return DayNightManager.shared.color(with: DAY_NIGHT_BACKGROUNDLIGHT) 99 | }) 100 | } 101 | class var highlight: LVThemeColor { 102 | return LVThemeColor.init(with: { () -> NSObject? in 103 | return DayNightManager.shared.color(with: DAY_NIGHT_HIGHLIGHT) 104 | }) 105 | } 106 | } 107 | 108 | extension LVThemeString { 109 | class var dayNightString: LVThemeString { 110 | return LVThemeString.init(with: { () -> NSObject? in 111 | return (DayNightManager.shared.night ? "黑夜" : "白天") as NSString 112 | }) 113 | } 114 | } 115 | 116 | extension LVThemeImage { 117 | class var dayNightImage: LVThemeImage { 118 | return LVThemeImage.init(with: { () -> NSObject? in 119 | return UIImage(named: DayNightManager.shared.night ? "24_yejian" : "24_rijian") 120 | }) 121 | } 122 | } 123 | 124 | class DayNightObserver: LVThemeKitObserverGenerator { 125 | var observer: Any? 126 | required init() { } 127 | func lvThemeKitObserverGenerator(completion: @escaping () -> Void) { 128 | observer = NotificationCenter.default.addObserver(forName: .dayNightChanged, object: nil, queue: OperationQueue.main) { (_) in 129 | completion() 130 | } 131 | } 132 | deinit { 133 | if let observer = observer { 134 | NotificationCenter.default.removeObserver(observer) 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/DayNightDemoSwift/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DayNightDemoSwift 4 | // 5 | // Created by lvpengwei on 2019/4/10. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import LVThemeKit 11 | 12 | class ViewController: UIViewController { 13 | @IBOutlet weak var changeBtn: UIButton! 14 | @IBOutlet weak var label: UILabel! 15 | @IBOutlet weak var imageView: UIImageView! 16 | @IBOutlet weak var slider: UISlider! 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | label.viewTK.theme.backgroundColor = LVThemeColor.background 20 | label.labelTK.theme.textColor = LVThemeColor.font 21 | slider.sliderTK.theme.minimumTrackTintColor = LVThemeColor.backgroundLight 22 | slider.sliderTK.theme.maximumTrackTintColor = LVThemeColor.highlight 23 | changeBtn.buttonTK.theme.setTitle(LVThemeString.dayNightString, for: .normal) 24 | changeBtn.buttonTK.theme.setImage(LVThemeImage.dayNightImage, for: .normal) 25 | imageView.imageTK.theme.image = LVThemeImage.dayNightImage 26 | } 27 | @IBAction func changeAction(_ sender: Any) { 28 | DayNightManager.change() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :ios, '8.0' 3 | 4 | inhibit_all_warnings! 5 | 6 | target 'DayNightDemoSwift' do 7 | use_frameworks! 8 | 9 | pod 'LVThemeKit/Swift', :path => '../../' 10 | end 11 | -------------------------------------------------------------------------------- /Examples/DayNightDemoSwift/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - LVThemeKit/Swift (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - LVThemeKit/Swift (from `../../`) 6 | 7 | EXTERNAL SOURCES: 8 | LVThemeKit: 9 | :path: "../../" 10 | 11 | SPEC CHECKSUMS: 12 | LVThemeKit: 90969fa20d54cf9bae01e953588bbb0337bbaaf6 13 | 14 | PODFILE CHECKSUM: c8fdf06b146eaf644545aaa0eb8e5af8f3ee1a2a 15 | 16 | COCOAPODS: 1.6.1 17 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9ACF914F9D01B6D0B7C27A66 /* libPods-LVThemeKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F55CBBE4695D144CDAEA270 /* libPods-LVThemeKit.a */; }; 11 | E63982C1224F694E00F29F99 /* UIColor+hex.m in Sources */ = {isa = PBXBuildFile; fileRef = E63982C0224F694E00F29F99 /* UIColor+hex.m */; }; 12 | E69DFC7B225B33DB00E77C71 /* ThemeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E69DFC74225B33DB00E77C71 /* ThemeManager.m */; }; 13 | E69DFC7C225B33DB00E77C71 /* QDThemeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E69DFC75225B33DB00E77C71 /* QDThemeManager.m */; }; 14 | E69DFC7D225B33DB00E77C71 /* AppThemeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E69DFC78225B33DB00E77C71 /* AppThemeManager.m */; }; 15 | E69DFC7E225B33DB00E77C71 /* ReaderThemeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E69DFC7A225B33DB00E77C71 /* ReaderThemeManager.m */; }; 16 | E69DFC82225B34CD00E77C71 /* LVThemeColor+Demo.m in Sources */ = {isa = PBXBuildFile; fileRef = E69DFC81225B34CD00E77C71 /* LVThemeColor+Demo.m */; }; 17 | E69DFC85225B350F00E77C71 /* LVThemeString+Demo.m in Sources */ = {isa = PBXBuildFile; fileRef = E69DFC84225B350F00E77C71 /* LVThemeString+Demo.m */; }; 18 | E69DFC88225B356700E77C71 /* LVThemeKit+Demo.m in Sources */ = {isa = PBXBuildFile; fileRef = E69DFC87225B356700E77C71 /* LVThemeKit+Demo.m */; }; 19 | E69DFCA0225B564900E77C71 /* UIViewWithLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = E69DFC9F225B564900E77C71 /* UIViewWithLayer.m */; }; 20 | E6CC1BCC224F5DDB00A5C282 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E6CC1BCB224F5DDB00A5C282 /* AppDelegate.m */; }; 21 | E6CC1BCF224F5DDB00A5C282 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E6CC1BCE224F5DDB00A5C282 /* ViewController.m */; }; 22 | E6CC1BD2224F5DDB00A5C282 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E6CC1BD0224F5DDB00A5C282 /* Main.storyboard */; }; 23 | E6CC1BD4224F5DDB00A5C282 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E6CC1BD3224F5DDB00A5C282 /* Assets.xcassets */; }; 24 | E6CC1BD7224F5DDB00A5C282 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E6CC1BD5224F5DDB00A5C282 /* LaunchScreen.storyboard */; }; 25 | E6CC1BDA224F5DDB00A5C282 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E6CC1BD9224F5DDB00A5C282 /* main.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 2778D6590DD31F2C73B7C983 /* Pods-LVThemeKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LVThemeKit.debug.xcconfig"; path = "Target Support Files/Pods-LVThemeKit/Pods-LVThemeKit.debug.xcconfig"; sourceTree = ""; }; 30 | 3F55CBBE4695D144CDAEA270 /* libPods-LVThemeKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LVThemeKit.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | AAA655D5B147233FAAB3A9E8 /* Pods-LVThemeKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LVThemeKit.release.xcconfig"; path = "Target Support Files/Pods-LVThemeKit/Pods-LVThemeKit.release.xcconfig"; sourceTree = ""; }; 32 | E63982BF224F694E00F29F99 /* UIColor+hex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIColor+hex.h"; sourceTree = ""; }; 33 | E63982C0224F694E00F29F99 /* UIColor+hex.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIColor+hex.m"; sourceTree = ""; }; 34 | E69DFC73225B33DB00E77C71 /* AppThemeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppThemeManager.h; sourceTree = ""; }; 35 | E69DFC74225B33DB00E77C71 /* ThemeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThemeManager.m; sourceTree = ""; }; 36 | E69DFC75225B33DB00E77C71 /* QDThemeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QDThemeManager.m; sourceTree = ""; }; 37 | E69DFC76225B33DB00E77C71 /* ReaderThemeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderThemeManager.h; sourceTree = ""; }; 38 | E69DFC77225B33DB00E77C71 /* ThemeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThemeManager.h; sourceTree = ""; }; 39 | E69DFC78225B33DB00E77C71 /* AppThemeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppThemeManager.m; sourceTree = ""; }; 40 | E69DFC79225B33DB00E77C71 /* QDThemeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QDThemeManager.h; sourceTree = ""; }; 41 | E69DFC7A225B33DB00E77C71 /* ReaderThemeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderThemeManager.m; sourceTree = ""; }; 42 | E69DFC80225B34CD00E77C71 /* LVThemeColor+Demo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LVThemeColor+Demo.h"; sourceTree = ""; }; 43 | E69DFC81225B34CD00E77C71 /* LVThemeColor+Demo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LVThemeColor+Demo.m"; sourceTree = ""; }; 44 | E69DFC83225B350F00E77C71 /* LVThemeString+Demo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LVThemeString+Demo.h"; sourceTree = ""; }; 45 | E69DFC84225B350F00E77C71 /* LVThemeString+Demo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LVThemeString+Demo.m"; sourceTree = ""; }; 46 | E69DFC86225B356700E77C71 /* LVThemeKit+Demo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LVThemeKit+Demo.h"; sourceTree = ""; }; 47 | E69DFC87225B356700E77C71 /* LVThemeKit+Demo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LVThemeKit+Demo.m"; sourceTree = ""; }; 48 | E69DFC9E225B564900E77C71 /* UIViewWithLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIViewWithLayer.h; sourceTree = ""; }; 49 | E69DFC9F225B564900E77C71 /* UIViewWithLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIViewWithLayer.m; sourceTree = ""; }; 50 | E6CC1BC7224F5DDB00A5C282 /* LVThemeKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LVThemeKit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E6CC1BCA224F5DDB00A5C282 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 52 | E6CC1BCB224F5DDB00A5C282 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 53 | E6CC1BCD224F5DDB00A5C282 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 54 | E6CC1BCE224F5DDB00A5C282 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 55 | E6CC1BD1224F5DDB00A5C282 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 56 | E6CC1BD3224F5DDB00A5C282 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 57 | E6CC1BD6224F5DDB00A5C282 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 58 | E6CC1BD8224F5DDB00A5C282 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | E6CC1BD9224F5DDB00A5C282 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 60 | /* End PBXFileReference section */ 61 | 62 | /* Begin PBXFrameworksBuildPhase section */ 63 | E6CC1BC4224F5DDB00A5C282 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | 9ACF914F9D01B6D0B7C27A66 /* libPods-LVThemeKit.a in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 6667A176B7755014C311F1EB /* Pods */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 2778D6590DD31F2C73B7C983 /* Pods-LVThemeKit.debug.xcconfig */, 78 | AAA655D5B147233FAAB3A9E8 /* Pods-LVThemeKit.release.xcconfig */, 79 | ); 80 | path = Pods; 81 | sourceTree = ""; 82 | }; 83 | E69DFC72225B33DB00E77C71 /* ThemeManager */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | E69DFC73225B33DB00E77C71 /* AppThemeManager.h */, 87 | E69DFC78225B33DB00E77C71 /* AppThemeManager.m */, 88 | E69DFC77225B33DB00E77C71 /* ThemeManager.h */, 89 | E69DFC74225B33DB00E77C71 /* ThemeManager.m */, 90 | E69DFC79225B33DB00E77C71 /* QDThemeManager.h */, 91 | E69DFC75225B33DB00E77C71 /* QDThemeManager.m */, 92 | E69DFC76225B33DB00E77C71 /* ReaderThemeManager.h */, 93 | E69DFC7A225B33DB00E77C71 /* ReaderThemeManager.m */, 94 | ); 95 | path = ThemeManager; 96 | sourceTree = ""; 97 | }; 98 | E69DFC7F225B34B700E77C71 /* Category */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | E69DFC80225B34CD00E77C71 /* LVThemeColor+Demo.h */, 102 | E69DFC81225B34CD00E77C71 /* LVThemeColor+Demo.m */, 103 | E69DFC83225B350F00E77C71 /* LVThemeString+Demo.h */, 104 | E69DFC84225B350F00E77C71 /* LVThemeString+Demo.m */, 105 | E69DFC86225B356700E77C71 /* LVThemeKit+Demo.h */, 106 | E69DFC87225B356700E77C71 /* LVThemeKit+Demo.m */, 107 | ); 108 | path = Category; 109 | sourceTree = ""; 110 | }; 111 | E6CC1BBE224F5DDB00A5C282 = { 112 | isa = PBXGroup; 113 | children = ( 114 | E6CC1BC9224F5DDB00A5C282 /* LVThemeKit */, 115 | E6CC1BC8224F5DDB00A5C282 /* Products */, 116 | 6667A176B7755014C311F1EB /* Pods */, 117 | F9BCB040BBF226589F3E507C /* Frameworks */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | E6CC1BC8224F5DDB00A5C282 /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | E6CC1BC7224F5DDB00A5C282 /* LVThemeKit.app */, 125 | ); 126 | name = Products; 127 | sourceTree = ""; 128 | }; 129 | E6CC1BC9224F5DDB00A5C282 /* LVThemeKit */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | E69DFC7F225B34B700E77C71 /* Category */, 133 | E69DFC72225B33DB00E77C71 /* ThemeManager */, 134 | E6CC1C04224F666400A5C282 /* Util */, 135 | E6CC1BCA224F5DDB00A5C282 /* AppDelegate.h */, 136 | E6CC1BCB224F5DDB00A5C282 /* AppDelegate.m */, 137 | E6CC1BCD224F5DDB00A5C282 /* ViewController.h */, 138 | E6CC1BCE224F5DDB00A5C282 /* ViewController.m */, 139 | E6CC1BD0224F5DDB00A5C282 /* Main.storyboard */, 140 | E6CC1BD3224F5DDB00A5C282 /* Assets.xcassets */, 141 | E6CC1BD5224F5DDB00A5C282 /* LaunchScreen.storyboard */, 142 | E6CC1BD8224F5DDB00A5C282 /* Info.plist */, 143 | E6CC1BD9224F5DDB00A5C282 /* main.m */, 144 | E69DFC9E225B564900E77C71 /* UIViewWithLayer.h */, 145 | E69DFC9F225B564900E77C71 /* UIViewWithLayer.m */, 146 | ); 147 | path = LVThemeKit; 148 | sourceTree = ""; 149 | }; 150 | E6CC1C04224F666400A5C282 /* Util */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | E63982BF224F694E00F29F99 /* UIColor+hex.h */, 154 | E63982C0224F694E00F29F99 /* UIColor+hex.m */, 155 | ); 156 | path = Util; 157 | sourceTree = ""; 158 | }; 159 | F9BCB040BBF226589F3E507C /* Frameworks */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 3F55CBBE4695D144CDAEA270 /* libPods-LVThemeKit.a */, 163 | ); 164 | name = Frameworks; 165 | sourceTree = ""; 166 | }; 167 | /* End PBXGroup section */ 168 | 169 | /* Begin PBXNativeTarget section */ 170 | E6CC1BC6224F5DDB00A5C282 /* LVThemeKit */ = { 171 | isa = PBXNativeTarget; 172 | buildConfigurationList = E6CC1BDD224F5DDB00A5C282 /* Build configuration list for PBXNativeTarget "LVThemeKit" */; 173 | buildPhases = ( 174 | 73D6212142ABB7BD331E33D2 /* [CP] Check Pods Manifest.lock */, 175 | E6CC1BC3224F5DDB00A5C282 /* Sources */, 176 | E6CC1BC4224F5DDB00A5C282 /* Frameworks */, 177 | E6CC1BC5224F5DDB00A5C282 /* Resources */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | ); 183 | name = LVThemeKit; 184 | productName = LVThemeKit; 185 | productReference = E6CC1BC7224F5DDB00A5C282 /* LVThemeKit.app */; 186 | productType = "com.apple.product-type.application"; 187 | }; 188 | /* End PBXNativeTarget section */ 189 | 190 | /* Begin PBXProject section */ 191 | E6CC1BBF224F5DDB00A5C282 /* Project object */ = { 192 | isa = PBXProject; 193 | attributes = { 194 | LastUpgradeCheck = 1020; 195 | ORGANIZATIONNAME = lvpengwei; 196 | TargetAttributes = { 197 | E6CC1BC6224F5DDB00A5C282 = { 198 | CreatedOnToolsVersion = 10.2; 199 | }; 200 | }; 201 | }; 202 | buildConfigurationList = E6CC1BC2224F5DDB00A5C282 /* Build configuration list for PBXProject "LVThemeKit" */; 203 | compatibilityVersion = "Xcode 9.3"; 204 | developmentRegion = en; 205 | hasScannedForEncodings = 0; 206 | knownRegions = ( 207 | en, 208 | Base, 209 | ); 210 | mainGroup = E6CC1BBE224F5DDB00A5C282; 211 | productRefGroup = E6CC1BC8224F5DDB00A5C282 /* Products */; 212 | projectDirPath = ""; 213 | projectRoot = ""; 214 | targets = ( 215 | E6CC1BC6224F5DDB00A5C282 /* LVThemeKit */, 216 | ); 217 | }; 218 | /* End PBXProject section */ 219 | 220 | /* Begin PBXResourcesBuildPhase section */ 221 | E6CC1BC5224F5DDB00A5C282 /* Resources */ = { 222 | isa = PBXResourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | E6CC1BD7224F5DDB00A5C282 /* LaunchScreen.storyboard in Resources */, 226 | E6CC1BD4224F5DDB00A5C282 /* Assets.xcassets in Resources */, 227 | E6CC1BD2224F5DDB00A5C282 /* Main.storyboard in Resources */, 228 | ); 229 | runOnlyForDeploymentPostprocessing = 0; 230 | }; 231 | /* End PBXResourcesBuildPhase section */ 232 | 233 | /* Begin PBXShellScriptBuildPhase section */ 234 | 73D6212142ABB7BD331E33D2 /* [CP] Check Pods Manifest.lock */ = { 235 | isa = PBXShellScriptBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | ); 239 | inputFileListPaths = ( 240 | ); 241 | inputPaths = ( 242 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 243 | "${PODS_ROOT}/Manifest.lock", 244 | ); 245 | name = "[CP] Check Pods Manifest.lock"; 246 | outputFileListPaths = ( 247 | ); 248 | outputPaths = ( 249 | "$(DERIVED_FILE_DIR)/Pods-LVThemeKit-checkManifestLockResult.txt", 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | shellPath = /bin/sh; 253 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 254 | showEnvVarsInLog = 0; 255 | }; 256 | /* End PBXShellScriptBuildPhase section */ 257 | 258 | /* Begin PBXSourcesBuildPhase section */ 259 | E6CC1BC3224F5DDB00A5C282 /* Sources */ = { 260 | isa = PBXSourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | E69DFC7E225B33DB00E77C71 /* ReaderThemeManager.m in Sources */, 264 | E69DFC7C225B33DB00E77C71 /* QDThemeManager.m in Sources */, 265 | E69DFC7D225B33DB00E77C71 /* AppThemeManager.m in Sources */, 266 | E63982C1224F694E00F29F99 /* UIColor+hex.m in Sources */, 267 | E6CC1BCF224F5DDB00A5C282 /* ViewController.m in Sources */, 268 | E69DFC7B225B33DB00E77C71 /* ThemeManager.m in Sources */, 269 | E6CC1BDA224F5DDB00A5C282 /* main.m in Sources */, 270 | E6CC1BCC224F5DDB00A5C282 /* AppDelegate.m in Sources */, 271 | E69DFC82225B34CD00E77C71 /* LVThemeColor+Demo.m in Sources */, 272 | E69DFC85225B350F00E77C71 /* LVThemeString+Demo.m in Sources */, 273 | E69DFCA0225B564900E77C71 /* UIViewWithLayer.m in Sources */, 274 | E69DFC88225B356700E77C71 /* LVThemeKit+Demo.m in Sources */, 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | }; 278 | /* End PBXSourcesBuildPhase section */ 279 | 280 | /* Begin PBXVariantGroup section */ 281 | E6CC1BD0224F5DDB00A5C282 /* Main.storyboard */ = { 282 | isa = PBXVariantGroup; 283 | children = ( 284 | E6CC1BD1224F5DDB00A5C282 /* Base */, 285 | ); 286 | name = Main.storyboard; 287 | sourceTree = ""; 288 | }; 289 | E6CC1BD5224F5DDB00A5C282 /* LaunchScreen.storyboard */ = { 290 | isa = PBXVariantGroup; 291 | children = ( 292 | E6CC1BD6224F5DDB00A5C282 /* Base */, 293 | ); 294 | name = LaunchScreen.storyboard; 295 | sourceTree = ""; 296 | }; 297 | /* End PBXVariantGroup section */ 298 | 299 | /* Begin XCBuildConfiguration section */ 300 | E6CC1BDB224F5DDB00A5C282 /* Debug */ = { 301 | isa = XCBuildConfiguration; 302 | buildSettings = { 303 | ALWAYS_SEARCH_USER_PATHS = NO; 304 | CLANG_ANALYZER_NONNULL = YES; 305 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 307 | CLANG_CXX_LIBRARY = "libc++"; 308 | CLANG_ENABLE_MODULES = YES; 309 | CLANG_ENABLE_OBJC_ARC = YES; 310 | CLANG_ENABLE_OBJC_WEAK = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INFINITE_RECURSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 324 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 327 | CLANG_WARN_STRICT_PROTOTYPES = YES; 328 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 329 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 330 | CLANG_WARN_UNREACHABLE_CODE = YES; 331 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 332 | CODE_SIGN_IDENTITY = "iPhone Developer"; 333 | COPY_PHASE_STRIP = NO; 334 | DEBUG_INFORMATION_FORMAT = dwarf; 335 | ENABLE_STRICT_OBJC_MSGSEND = YES; 336 | ENABLE_TESTABILITY = YES; 337 | GCC_C_LANGUAGE_STANDARD = gnu11; 338 | GCC_DYNAMIC_NO_PIC = NO; 339 | GCC_NO_COMMON_BLOCKS = YES; 340 | GCC_OPTIMIZATION_LEVEL = 0; 341 | GCC_PREPROCESSOR_DEFINITIONS = ( 342 | "DEBUG=1", 343 | "$(inherited)", 344 | ); 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 352 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 353 | MTL_FAST_MATH = YES; 354 | ONLY_ACTIVE_ARCH = YES; 355 | SDKROOT = iphoneos; 356 | }; 357 | name = Debug; 358 | }; 359 | E6CC1BDC224F5DDB00A5C282 /* Release */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ALWAYS_SEARCH_USER_PATHS = NO; 363 | CLANG_ANALYZER_NONNULL = YES; 364 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 365 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 366 | CLANG_CXX_LIBRARY = "libc++"; 367 | CLANG_ENABLE_MODULES = YES; 368 | CLANG_ENABLE_OBJC_ARC = YES; 369 | CLANG_ENABLE_OBJC_WEAK = YES; 370 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 371 | CLANG_WARN_BOOL_CONVERSION = YES; 372 | CLANG_WARN_COMMA = YES; 373 | CLANG_WARN_CONSTANT_CONVERSION = YES; 374 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 375 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 376 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 377 | CLANG_WARN_EMPTY_BODY = YES; 378 | CLANG_WARN_ENUM_CONVERSION = YES; 379 | CLANG_WARN_INFINITE_RECURSION = YES; 380 | CLANG_WARN_INT_CONVERSION = YES; 381 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 382 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 383 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 384 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 385 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 386 | CLANG_WARN_STRICT_PROTOTYPES = YES; 387 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 388 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 389 | CLANG_WARN_UNREACHABLE_CODE = YES; 390 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 391 | CODE_SIGN_IDENTITY = "iPhone Developer"; 392 | COPY_PHASE_STRIP = NO; 393 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 394 | ENABLE_NS_ASSERTIONS = NO; 395 | ENABLE_STRICT_OBJC_MSGSEND = YES; 396 | GCC_C_LANGUAGE_STANDARD = gnu11; 397 | GCC_NO_COMMON_BLOCKS = YES; 398 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 399 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 400 | GCC_WARN_UNDECLARED_SELECTOR = YES; 401 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 402 | GCC_WARN_UNUSED_FUNCTION = YES; 403 | GCC_WARN_UNUSED_VARIABLE = YES; 404 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 405 | MTL_ENABLE_DEBUG_INFO = NO; 406 | MTL_FAST_MATH = YES; 407 | SDKROOT = iphoneos; 408 | VALIDATE_PRODUCT = YES; 409 | }; 410 | name = Release; 411 | }; 412 | E6CC1BDE224F5DDB00A5C282 /* Debug */ = { 413 | isa = XCBuildConfiguration; 414 | baseConfigurationReference = 2778D6590DD31F2C73B7C983 /* Pods-LVThemeKit.debug.xcconfig */; 415 | buildSettings = { 416 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 417 | CODE_SIGN_STYLE = Automatic; 418 | DEVELOPMENT_TEAM = 3A75DREAB8; 419 | INFOPLIST_FILE = LVThemeKit/Info.plist; 420 | LD_RUNPATH_SEARCH_PATHS = ( 421 | "$(inherited)", 422 | "@executable_path/Frameworks", 423 | ); 424 | PRODUCT_BUNDLE_IDENTIFIER = com.lvpengwei.LVThemeKit; 425 | PRODUCT_NAME = "$(TARGET_NAME)"; 426 | TARGETED_DEVICE_FAMILY = "1,2"; 427 | }; 428 | name = Debug; 429 | }; 430 | E6CC1BDF224F5DDB00A5C282 /* Release */ = { 431 | isa = XCBuildConfiguration; 432 | baseConfigurationReference = AAA655D5B147233FAAB3A9E8 /* Pods-LVThemeKit.release.xcconfig */; 433 | buildSettings = { 434 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 435 | CODE_SIGN_STYLE = Automatic; 436 | DEVELOPMENT_TEAM = 3A75DREAB8; 437 | INFOPLIST_FILE = LVThemeKit/Info.plist; 438 | LD_RUNPATH_SEARCH_PATHS = ( 439 | "$(inherited)", 440 | "@executable_path/Frameworks", 441 | ); 442 | PRODUCT_BUNDLE_IDENTIFIER = com.lvpengwei.LVThemeKit; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | TARGETED_DEVICE_FAMILY = "1,2"; 445 | }; 446 | name = Release; 447 | }; 448 | /* End XCBuildConfiguration section */ 449 | 450 | /* Begin XCConfigurationList section */ 451 | E6CC1BC2224F5DDB00A5C282 /* Build configuration list for PBXProject "LVThemeKit" */ = { 452 | isa = XCConfigurationList; 453 | buildConfigurations = ( 454 | E6CC1BDB224F5DDB00A5C282 /* Debug */, 455 | E6CC1BDC224F5DDB00A5C282 /* Release */, 456 | ); 457 | defaultConfigurationIsVisible = 0; 458 | defaultConfigurationName = Release; 459 | }; 460 | E6CC1BDD224F5DDB00A5C282 /* Build configuration list for PBXNativeTarget "LVThemeKit" */ = { 461 | isa = XCConfigurationList; 462 | buildConfigurations = ( 463 | E6CC1BDE224F5DDB00A5C282 /* Debug */, 464 | E6CC1BDF224F5DDB00A5C282 /* Release */, 465 | ); 466 | defaultConfigurationIsVisible = 0; 467 | defaultConfigurationName = Release; 468 | }; 469 | /* End XCConfigurationList section */ 470 | }; 471 | rootObject = E6CC1BBF224F5DDB00A5C282 /* Project object */; 472 | } 473 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit.xcodeproj/xcshareddata/xcschemes/LVThemeKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "LVThemeKit+Demo.h" 11 | 12 | @interface AppDelegate () 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | [LVThemeKit demoConfig]; 19 | return YES; 20 | } 21 | 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 26 | } 27 | 28 | 29 | - (void)applicationDidEnterBackground:(UIApplication *)application { 30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | 35 | - (void)applicationWillEnterForeground:(UIApplication *)application { 36 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | 40 | - (void)applicationDidBecomeActive:(UIApplication *)application { 41 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | } 43 | 44 | 45 | - (void)applicationWillTerminate:(UIApplication *)application { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 47 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 67 | 74 | 80 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Category/LVThemeColor+Demo.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeColor+Demo.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeColor.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface LVThemeColor (Demo) 14 | + (instancetype)readerThemeFont; 15 | + (instancetype)readerThemeBackground; 16 | + (instancetype)appThemeFont; 17 | + (instancetype)appThemeBackground; 18 | + (instancetype)qdThemeFont; 19 | + (instancetype)qdThemeBackground; 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Category/LVThemeColor+Demo.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeColor+Demo.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeColor+Demo.h" 10 | #import "AppThemeManager.h" 11 | #import "ReaderThemeManager.h" 12 | #import "QDThemeManager.h" 13 | 14 | @implementation LVThemeColor (Demo) 15 | + (instancetype)readerThemeFont { 16 | return [self readerTheme:READER_THEME_FONT]; 17 | } 18 | + (instancetype)readerThemeBackground { 19 | return [self readerTheme:READER_THEME_BACKGROUND]; 20 | } 21 | + (instancetype)appThemeFont { 22 | return [self appTheme:APP_THEME_FONT]; 23 | } 24 | + (instancetype)appThemeBackground { 25 | return [self appTheme:APP_THEME_BACKGROUND]; 26 | } 27 | + (instancetype)readerTheme:(NSString *)key { 28 | return [self instance:^id _Nonnull{ 29 | return [[ReaderThemeManager share] colorWithKey:key]; 30 | }]; 31 | } 32 | + (instancetype)appTheme:(NSString *)key { 33 | return [self instance:^id _Nonnull{ 34 | return [[AppThemeManager share] colorWithKey:key]; 35 | }]; 36 | } 37 | + (instancetype)qdThemeFont { 38 | return [self qdTheme:QD_THEME_FONT]; 39 | } 40 | + (instancetype)qdThemeBackground { 41 | return [self qdTheme:QD_THEME_BACKGROUND]; 42 | } 43 | + (instancetype)qdTheme:(NSString *)key { 44 | return [self instance:^id _Nonnull{ 45 | return [[QDThemeManager share] colorWithKey:key]; 46 | }]; 47 | } 48 | @end 49 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Category/LVThemeKit+Demo.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeKit+Demo.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeKit.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface LVThemeKit (Demo) 14 | @property (nonatomic, strong, readonly) T qd; // 黑夜模式 15 | @property (nonatomic, strong, readonly) T reader; // 阅读器主题 16 | @property (nonatomic, strong, readonly) T app; // app皮肤 17 | + (void)demoConfig; 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Category/LVThemeKit+Demo.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeKit+Demo.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeKit+Demo.h" 10 | #import "QDThemeManager.h" 11 | #import "ReaderThemeManager.h" 12 | #import "AppThemeManager.h" 13 | 14 | @interface _Observer : NSObject 15 | @property (nonatomic, strong) id ob; 16 | - (NSString *)notificationName; 17 | @end 18 | @implementation _Observer 19 | - (NSString *)notificationName { 20 | return @""; 21 | } 22 | - (void)lvThemeKitObserverGeneratorCompletion:(LVThemeKitCompletion)completion { 23 | self.ob = [[NSNotificationCenter defaultCenter] addObserverForName:[self notificationName] object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { 24 | completion(); 25 | }]; 26 | } 27 | - (void)dealloc { 28 | if (self.ob) [[NSNotificationCenter defaultCenter] removeObserver:self.ob]; 29 | } 30 | @end 31 | 32 | @interface QDObserver : _Observer 33 | @end 34 | @implementation QDObserver 35 | - (NSString *)notificationName { 36 | return QD_THEME_CHANGED_NOTIFICATION; 37 | } 38 | @end 39 | 40 | @interface ReaderObserver : _Observer 41 | @end 42 | @implementation ReaderObserver 43 | - (NSString *)notificationName { 44 | return READER_THEME_CHANGED_NOTIFICATION; 45 | } 46 | @end 47 | 48 | @interface AppObserver : _Observer 49 | @end 50 | @implementation AppObserver 51 | - (NSString *)notificationName { 52 | return APP_THEME_CHANGED_NOTIFICATION; 53 | } 54 | @end 55 | 56 | @implementation LVThemeKit (Demo) 57 | - (LVThemeObject *)qd { 58 | return self.themes[0]; 59 | } 60 | - (LVThemeObject *)reader { 61 | return self.themes[1]; 62 | } 63 | - (LVThemeObject *)app { 64 | return self.themes[2]; 65 | } 66 | + (void)demoConfig { 67 | LVThemeKitConfig *config = [[LVThemeKitConfig alloc] init]; 68 | config.generators = @[[QDObserver class], [ReaderObserver class], [AppObserver class]]; 69 | config.applyProperty = ^(LVThemeKit *tk, NSString * _Nonnull key, LVThemeKitApplyPropertyCompletion _Nonnull completion) { 70 | if ([QDThemeManager share].isNight && [tk.qd hasValueForKey:key]) { 71 | completion(tk.qd); 72 | } else if ([tk.reader hasValueForKey:key]) { 73 | completion(tk.reader); 74 | } else if ([tk.app hasValueForKey:key]) { 75 | completion(tk.app); 76 | } else if ([tk.qd hasValueForKey:key]) { 77 | completion(tk.qd); 78 | } 79 | }; 80 | LVThemeKit.config = config; 81 | } 82 | @end 83 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Category/LVThemeString+Demo.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeString+Demo.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeString.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface LVThemeString (Demo) 14 | + (instancetype)qdThemeString; 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Category/LVThemeString+Demo.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeString+Demo.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeString+Demo.h" 10 | #import "QDThemeManager.h" 11 | 12 | @implementation LVThemeString (Demo) 13 | + (instancetype)qdThemeString { 14 | return [self instance:^id _Nonnull{ 15 | return [QDThemeManager share].isNight ? @"黑夜Button" : @"白天Button"; 16 | }]; 17 | } 18 | @end 19 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ThemeManager/AppThemeManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppThemeManager.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ThemeManager.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | // APP 皮肤 15 | extern NSString * const APP_THEME_CHANGED_NOTIFICATION; 16 | extern NSString * const APP_THEME_FONT; 17 | extern NSString * const APP_THEME_BACKGROUND; 18 | 19 | @interface AppThemeManager : ThemeManager 20 | + (instancetype)share; 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ThemeManager/AppThemeManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppThemeManager.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "AppThemeManager.h" 10 | #import "UIColor+hex.h" 11 | 12 | NSString * const APP_THEME_CHANGED_NOTIFICATION = @"app_theme_changed"; 13 | NSString * const APP_THEME_FONT = @"font"; 14 | NSString * const APP_THEME_BACKGROUND = @"background"; 15 | 16 | @implementation AppThemeManager 17 | + (instancetype)share { 18 | static dispatch_once_t onceToken; 19 | static AppThemeManager *themeManager; 20 | dispatch_once(&onceToken, ^{ 21 | themeManager = [[self alloc] init]; 22 | }); 23 | return themeManager; 24 | } 25 | - (instancetype)init { 26 | self = [super init]; 27 | if (self) { 28 | self.themes = @[ 29 | @{ APP_THEME_FONT: @"#00FF00", APP_THEME_BACKGROUND: @"#D2E4D2", }, 30 | @{ APP_THEME_FONT: @"#FF00FF", APP_THEME_BACKGROUND: @"#FFE4E1", } 31 | ]; 32 | } 33 | return self; 34 | } 35 | - (void)themeIndexChanged { 36 | [[NSNotificationCenter defaultCenter] postNotificationName:APP_THEME_CHANGED_NOTIFICATION object:nil]; 37 | } 38 | @end 39 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ThemeManager/QDThemeManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // QDThemeManager.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIKit/UIKit.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | // 黑夜模式 15 | extern NSString * const QD_THEME_CHANGED_NOTIFICATION; 16 | extern NSString * const QD_THEME_FONT; 17 | extern NSString * const QD_THEME_BACKGROUND; 18 | 19 | typedef enum : NSUInteger { 20 | QDThemeDay, 21 | QDThemeNight, 22 | } QDThemeType; 23 | @interface QDThemeManager : NSObject 24 | @property (nonatomic) QDThemeType type; 25 | @property (nonatomic, readonly, getter=isNight) BOOL night; 26 | + (instancetype)share; 27 | - (UIColor *)colorWithKey:(NSString *)key; 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ThemeManager/QDThemeManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // QDThemeManager.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "QDThemeManager.h" 10 | #import "UIColor+hex.h" 11 | 12 | NSString * const QD_THEME_CHANGED_NOTIFICATION = @"qd_theme_changed"; 13 | NSString * const QD_THEME_FONT = @"font"; 14 | NSString * const QD_THEME_BACKGROUND = @"background"; 15 | 16 | @interface QDThemeManager () 17 | @property (nonatomic, strong) NSArray *themes; 18 | @end 19 | @implementation QDThemeManager 20 | + (instancetype)share { 21 | static dispatch_once_t onceToken; 22 | static QDThemeManager *themeManager; 23 | dispatch_once(&onceToken, ^{ 24 | themeManager = [[self alloc] init]; 25 | }); 26 | return themeManager; 27 | } 28 | - (instancetype)init { 29 | self = [super init]; 30 | if (self) { 31 | self.type = QDThemeDay; 32 | self.themes = @[ 33 | @{ QD_THEME_FONT: @"#3B3F47", QD_THEME_BACKGROUND: @"#FFFFFF", }, 34 | @{ QD_THEME_FONT: @"#CCCCCC", QD_THEME_BACKGROUND: @"#262626", } 35 | ]; 36 | } 37 | return self; 38 | } 39 | - (BOOL)isNight { 40 | return self.type == QDThemeNight; 41 | } 42 | - (void)setType:(QDThemeType)type { 43 | _type = type; 44 | [[NSNotificationCenter defaultCenter] postNotificationName:QD_THEME_CHANGED_NOTIFICATION object:nil]; 45 | } 46 | - (UIColor *)colorWithKey:(NSString *)key { 47 | NSInteger index = _type == QDThemeDay ? 0 : 1; 48 | return [UIColor colorWithHex:self.themes[index][key]]; 49 | } 50 | @end 51 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ThemeManager/ReaderThemeManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // ReaderThemeManager.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ThemeManager.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | // 阅读器皮肤 15 | extern NSString * const READER_THEME_CHANGED_NOTIFICATION; 16 | extern NSString * const READER_THEME_FONT; 17 | extern NSString * const READER_THEME_BACKGROUND; 18 | 19 | @interface ReaderThemeManager : ThemeManager 20 | + (instancetype)share; 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ThemeManager/ReaderThemeManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // ReaderThemeManager.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "ReaderThemeManager.h" 10 | 11 | NSString * const READER_THEME_CHANGED_NOTIFICATION = @"reader_theme_changed"; 12 | NSString * const READER_THEME_FONT = @"font"; 13 | NSString * const READER_THEME_BACKGROUND = @"background"; 14 | 15 | @implementation ReaderThemeManager 16 | + (instancetype)share { 17 | static dispatch_once_t onceToken; 18 | static ReaderThemeManager *themeManager; 19 | dispatch_once(&onceToken, ^{ 20 | themeManager = [[self alloc] init]; 21 | }); 22 | return themeManager; 23 | } 24 | - (instancetype)init { 25 | self = [super init]; 26 | if (self) { 27 | self.themes = @[ 28 | @{ READER_THEME_FONT: @"#00FFFF", READER_THEME_BACKGROUND: @"#F6F6F6", }, 29 | @{ READER_THEME_FONT: @"#FF0000", READER_THEME_BACKGROUND: @"#FAF0E6", } 30 | ]; 31 | } 32 | return self; 33 | } 34 | - (void)themeIndexChanged { 35 | [[NSNotificationCenter defaultCenter] postNotificationName:READER_THEME_CHANGED_NOTIFICATION object:nil]; 36 | } 37 | @end 38 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ThemeManager/ThemeManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // ThemeManager.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "UIKit/UIKit.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface ThemeManager : NSObject 14 | @property (nonatomic) NSInteger currentIndex; 15 | @property (nonatomic, strong) NSArray *themes; 16 | - (void)switchTheme; 17 | - (UIColor *)colorWithKey:(NSString *)key; 18 | - (void)themeIndexChanged; 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ThemeManager/ThemeManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // ThemeManager.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "ThemeManager.h" 10 | #import "UIColor+hex.h" 11 | 12 | @implementation ThemeManager 13 | - (void)switchTheme { 14 | self.currentIndex = (self.currentIndex + 1) % (self.themes.count); 15 | } 16 | - (UIColor *)colorWithKey:(NSString *)key { 17 | return [UIColor colorWithHex:self.themes[self.currentIndex][key]]; 18 | } 19 | - (void)setCurrentIndex:(NSInteger)currentIndex { 20 | _currentIndex = currentIndex; 21 | [self themeIndexChanged]; 22 | } 23 | - (void)themeIndexChanged { } 24 | @end 25 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/UIViewWithLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewWithLayer.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UIViewWithLayer : UIView 14 | 15 | @property (nonatomic, weak) CALayer *lay; 16 | @property (nonatomic, weak) CAShapeLayer *shapeLay; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/UIViewWithLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewWithLayer.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "UIViewWithLayer.h" 10 | #import "LVThemeKit+Demo.h" 11 | #import "CALayer+LVThemeKit.h" 12 | #import "CAShapeLayer+LVThemeKit.h" 13 | #import "LVThemeColor+Demo.h" 14 | 15 | @implementation UIViewWithLayer 16 | - (instancetype)initWithFrame:(CGRect)frame { 17 | self = [super initWithFrame:frame]; 18 | if (self) { 19 | [self commitInit]; 20 | } 21 | return self; 22 | } 23 | - (void)awakeFromNib { 24 | [super awakeFromNib]; 25 | [self commitInit]; 26 | } 27 | - (void)commitInit { 28 | CALayer *lay = [CALayer layer]; 29 | lay.layerTK.reader.backgroundColor = [LVThemeColor readerThemeBackground]; 30 | lay.layerTK.reader.borderColor = [LVThemeColor readerThemeFont]; 31 | lay.borderWidth = 1; 32 | [self.layer addSublayer:lay]; 33 | self.lay = lay; 34 | CAShapeLayer *shapeLay = [CAShapeLayer layer]; 35 | shapeLay.shapeTK.reader.fillColor = [LVThemeColor readerThemeFont]; 36 | [self.layer addSublayer:shapeLay]; 37 | self.shapeLay = shapeLay; 38 | } 39 | - (void)layoutSubviews { 40 | [super layoutSubviews]; 41 | self.lay.frame = CGRectMake(8, 8, self.bounds.size.width * 0.5 - 16, self.bounds.size.height - 16); 42 | self.shapeLay.frame = CGRectMake(self.bounds.size.width * 0.5 + 8, 8, self.bounds.size.width * 0.5 - 16, self.bounds.size.height - 16); 43 | CGFloat radius = self.shapeLay.bounds.size.height * 0.5; 44 | self.shapeLay.path = [UIBezierPath bezierPathWithRoundedRect:self.shapeLay.bounds byRoundingCorners:UIRectCornerTopLeft|UIRectCornerBottomLeft cornerRadii:CGSizeMake(radius, radius)].CGPath; 45 | } 46 | - (CGSize)intrinsicContentSize { 47 | return CGSizeMake([UIScreen mainScreen].bounds.size.width, 44); 48 | } 49 | @end 50 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Util/UIColor+hex.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+hex.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UIColor (hex) 14 | + (UIColor *)colorWithHex:(NSString *)hex; 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/Util/UIColor+hex.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+hex.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "UIColor+hex.h" 10 | 11 | @implementation NSString (LeftPadding) 12 | // taken from http://stackoverflow.com/questions/964322/padding-string-to-left-with-objective-c 13 | - (NSString *)stringByPaddingTheLeftToLength:(NSUInteger)newLength withString:(NSString *)padString startingAtIndex:(NSUInteger)padIndex { 14 | if ([self length] <= newLength) 15 | return [[@"" stringByPaddingToLength:newLength - [self length] withString:padString startingAtIndex:padIndex] stringByAppendingString:self]; 16 | else 17 | return [self copy]; 18 | } 19 | @end 20 | 21 | @implementation UIColor (hex) 22 | + (UIColor *)colorWithHex:(NSString *)hex { 23 | if (hex.length == 0) 24 | return [UIColor blackColor]; 25 | 26 | if ([hex characterAtIndex:0] == '#') 27 | hex = [hex substringFromIndex:1]; 28 | 29 | NSString *a, *r, *g, *b; 30 | 31 | NSUInteger len = hex.length; 32 | if (len == 6) { 33 | six: 34 | a = @"FF"; 35 | r = [hex substringWithRange:NSMakeRange(0, 2)]; 36 | g = [hex substringWithRange:NSMakeRange(2, 2)]; 37 | b = [hex substringWithRange:NSMakeRange(4, 2)]; 38 | } 39 | else if (len == 8) { 40 | eight: 41 | a = [hex substringWithRange:NSMakeRange(0, 2)]; 42 | r = [hex substringWithRange:NSMakeRange(2, 2)]; 43 | g = [hex substringWithRange:NSMakeRange(4, 2)]; 44 | b = [hex substringWithRange:NSMakeRange(6, 2)]; 45 | } 46 | else if (len == 3) { 47 | three: 48 | a = @"FF"; 49 | r = [hex substringWithRange:NSMakeRange(0, 1)]; 50 | r = [r stringByAppendingString:a]; 51 | g = [hex substringWithRange:NSMakeRange(1, 1)]; 52 | g = [g stringByAppendingString:a]; 53 | b = [hex substringWithRange:NSMakeRange(2, 1)]; 54 | b = [b stringByAppendingString:a]; 55 | } 56 | else if (len == 4) { 57 | a = [hex substringWithRange:NSMakeRange(0, 1)]; 58 | a = [a stringByAppendingString:a]; 59 | r = [hex substringWithRange:NSMakeRange(1, 1)]; 60 | r = [r stringByAppendingString:a]; 61 | g = [hex substringWithRange:NSMakeRange(2, 1)]; 62 | g = [g stringByAppendingString:a]; 63 | b = [hex substringWithRange:NSMakeRange(3, 1)]; 64 | b = [b stringByAppendingString:a]; 65 | } 66 | else if (len == 5 || len == 7) { 67 | hex = [@"0" stringByAppendingString:hex]; 68 | if (len == 5) goto six; 69 | goto eight; 70 | } 71 | else if (len < 3) { 72 | hex = [hex stringByPaddingTheLeftToLength:3 withString:@"0" startingAtIndex:0]; 73 | goto three; 74 | } 75 | else if (len > 8) { 76 | hex = [hex substringFromIndex:len-8]; 77 | goto eight; 78 | } 79 | else { 80 | a = @"FF"; 81 | r = @"00"; 82 | g = @"00"; 83 | b = @"00"; 84 | } 85 | 86 | // parse each component separately. This gives more accurate results than 87 | // throwing it all together in one string and use scanf on the global string. 88 | a = [@"0x" stringByAppendingString:a]; 89 | r = [@"0x" stringByAppendingString:r]; 90 | g = [@"0x" stringByAppendingString:g]; 91 | b = [@"0x" stringByAppendingString:b]; 92 | 93 | uint av, rv, gv, bv; 94 | sscanf([a cStringUsingEncoding:NSASCIIStringEncoding], "%x", &av); 95 | sscanf([r cStringUsingEncoding:NSASCIIStringEncoding], "%x", &rv); 96 | sscanf([g cStringUsingEncoding:NSASCIIStringEncoding], "%x", &gv); 97 | sscanf([b cStringUsingEncoding:NSASCIIStringEncoding], "%x", &bv); 98 | 99 | return [UIColor colorWithRed: rv / 255.f 100 | green: gv / 255.f 101 | blue: bv / 255.f 102 | alpha: av / 255.f]; 103 | } 104 | @end 105 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "UIView+LVThemeKit.h" 11 | #import "UILabel+LVThemeKit.h" 12 | #import "UIButton+LVThemeKit.h" 13 | #import "ReaderThemeManager.h" 14 | #import "AppThemeManager.h" 15 | #import "QDThemeManager.h" 16 | #import "LVThemeColor+Demo.h" 17 | #import "LVThemeString+Demo.h" 18 | #import "LVThemeKit+Demo.h" 19 | 20 | @interface ViewController () 21 | @property (weak, nonatomic) IBOutlet UIButton *readerThemeButton; 22 | @property (weak, nonatomic) IBOutlet UILabel *readerThemeLabel; 23 | @property (weak, nonatomic) IBOutlet UIButton *appThemeButton; 24 | @property (weak, nonatomic) IBOutlet UILabel *appThemeLabel; 25 | @property (weak, nonatomic) IBOutlet UIButton *qdThemeButton; 26 | @end 27 | 28 | @implementation ViewController 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | [self.readerThemeButton.buttonTK.reader setTitleColor:[LVThemeColor readerThemeFont] forState:UIControlStateNormal]; 32 | self.readerThemeLabel.labelTK.reader.textColor = [LVThemeColor readerThemeFont]; 33 | self.readerThemeLabel.viewTK.reader.backgroundColor = [LVThemeColor readerThemeBackground]; 34 | self.readerThemeLabel.labelTK.qd.textColor = [LVThemeColor qdThemeFont]; 35 | self.readerThemeLabel.viewTK.qd.backgroundColor = [LVThemeColor qdThemeBackground]; 36 | [self.appThemeButton.buttonTK.app setTitleColor:[LVThemeColor appThemeFont] forState:UIControlStateNormal]; 37 | self.appThemeLabel.labelTK.app.textColor = [LVThemeColor appThemeFont]; 38 | self.appThemeLabel.viewTK.app.backgroundColor = [LVThemeColor appThemeBackground]; 39 | self.appThemeLabel.labelTK.qd.textColor = [LVThemeColor qdThemeFont]; 40 | self.appThemeLabel.viewTK.qd.backgroundColor = [LVThemeColor qdThemeBackground]; 41 | self.qdThemeButton.viewTK.qd.backgroundColor = [LVThemeColor qdThemeBackground]; 42 | [self.qdThemeButton.buttonTK.qd setTitleColor:[LVThemeColor qdThemeFont] forState:UIControlStateNormal]; 43 | [self.qdThemeButton.buttonTK.qd setTitle:[LVThemeString qdThemeString] forState:UIControlStateNormal]; 44 | } 45 | - (IBAction)readerThemeAction:(id)sender { 46 | [[ReaderThemeManager share] switchTheme]; 47 | } 48 | - (IBAction)appThemeAction:(id)sender { 49 | [[AppThemeManager share] switchTheme]; 50 | } 51 | - (IBAction)qdThemeAction:(id)sender { 52 | [QDThemeManager share].type = [QDThemeManager share].isNight ? QDThemeDay : QDThemeNight; 53 | } 54 | - (IBAction)openNewPage:(id)sender { 55 | UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; 56 | ViewController * vc = (ViewController *)[sb instantiateViewControllerWithIdentifier:@"ViewController"]; 57 | [self.navigationController pushViewController:vc animated:true]; 58 | } 59 | @end 60 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/LVThemeKit/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :ios, '8.0' 3 | 4 | inhibit_all_warnings! 5 | 6 | target 'LVThemeKit' do 7 | pod 'LVThemeKit/ObjC', :path => '../../' 8 | end 9 | -------------------------------------------------------------------------------- /Examples/MultiThemesDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - LVThemeKit/ObjC (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - LVThemeKit/ObjC (from `../../`) 6 | 7 | EXTERNAL SOURCES: 8 | LVThemeKit: 9 | :path: "../../" 10 | 11 | SPEC CHECKSUMS: 12 | LVThemeKit: 90969fa20d54cf9bae01e953588bbb0337bbaaf6 13 | 14 | PODFILE CHECKSUM: 5f2f024d5fd59da7765b044f9c7531b2be5cd9ff 15 | 16 | COCOAPODS: 1.6.1 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 lvpengwei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LVThemeKit.podspec: -------------------------------------------------------------------------------- 1 | @version = "1.0.0" 2 | Pod::Spec.new do |s| 3 | s.name = "LVThemeKit" 4 | s.version = @version 5 | s.summary = "Theme kit." 6 | s.description = "Theme kit for UIKit." 7 | s.homepage = "https://github.com/lvpengwei/LVThemeKit" 8 | s.license = {:type => 'MIT', :file => 'LICENSE' } 9 | s.author = { "lvpengwei" => "pengwei.lv@gmail.com" } 10 | s.ios.deployment_target = '8.0' 11 | s.source = { :git => "https://github.com/lvpengwei/LVThemeKit.git", :tag => @version } 12 | s.swift_version = '5.0' 13 | s.default_subspec = 'ObjC' 14 | 15 | s.subspec 'ObjC' do |objc| 16 | objc.source_files = ['Source/ObjC/**/*.{h,m}'] 17 | objc.prefix_header_contents = <<-EOS 18 | #ifdef __OBJC__ 19 | #import 20 | #import 21 | #import "LVThemeColor.h" 22 | #import "LVThemeImage.h" 23 | #import "LVThemeKit.h" 24 | #import "LVThemeString.h" 25 | #import "UIButton+LVThemeKit.h" 26 | #import "UILabel+LVThemeKit.h" 27 | #import "UIView+LVThemeKit.h" 28 | #import "UISlider+LVThemeKit.h" 29 | #import "UIImageView+LVThemeKit.h" 30 | #import "CALayer+LVThemeKit.h" 31 | #import "CAShapeLayer+LVThemeKit.h" 32 | #endif 33 | EOS 34 | end 35 | 36 | s.subspec 'Swift' do |swift| 37 | swift.source_files = ['Source/Swift/**/*.swift'] 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # LVThemeKit 4 | 5 | [说明](https://github.com/lvpengwei/LVThemeKit/wiki/%E8%AF%B4%E6%98%8E "说明") 6 | 7 | App 多区域皮肤 UIKit Category 8 | 9 | `LVThemeKit/ObjC` 适用于 OC 项目 10 | 11 | `LVThemeKit/Swift` 适用于 Swift 项目 12 | 13 | ## 功能 14 | - UIKit Category 15 | - UIView 16 | - UIImageView 17 | - UILabel 18 | - UIButton 19 | - UISlider 20 | - QuartzCore Category 21 | - CALayer 22 | - CAShapeLayer 23 | - Resource 24 | - LVThemeColor 25 | - LVThemeImage 26 | - LVThemeString 27 | - LVThemeAttributedString 28 | - 其他,可以很容易的扩展 29 | 30 | ## 用法 31 | + UIView 32 | 33 | ```objc 34 | // OC 35 | self.label.viewTK.theme.backgroundColor = [LVThemeColor background]; 36 | self.label.viewTK.other.backgroundColor = [LVThemeColor otherBackground]; 37 | ``` 38 | ```swift 39 | // Swift 40 | label.viewTK.theme.backgroundColor = LVThemeColor.background 41 | label.viewTK.other.backgroundColor = LVThemeColor.otherBackground 42 | ``` 43 | + UIButton 44 | ```objc 45 | // OC 46 | [self.button.buttonTK.theme setImage:[LVThemeImage image] forState:UIControlStateNormal]; 47 | ``` 48 | ```swift 49 | // Swift 50 | button.buttonTK.theme.setImage(LVThemeImage.image, for: .normal) 51 | ``` 52 | + CALayer 53 | ```objc 54 | // OC 55 | self.layer.layerTK.theme.borderColor = [LVThemeColor background]; 56 | ``` 57 | ```swift 58 | // Swift 59 | layer.layerTK.theme.borderColor = LVthemeColor.background 60 | ``` 61 | 62 | ## 配置 63 | - `LVThemeKitConfig` 64 | - `generators`: 监听主题变化的通知数组,根据配置的数量生成主题数量 65 | - `applyProperty`: 当配置多套主题时,自定义应用顺序,默认是数组顺序 66 | ```objc 67 | // OC 68 | LVThemeKitConfig *conf = [[LVThemeKitConfig alloc] init]; 69 | config.generators = @[[DayNightObserver class]]; 70 | LVThemeKit.config = conf; 71 | ``` 72 | ```swift 73 | // Swift 74 | let conf = LVThemeKitConfig() 75 | conf.generators = [DayNightObserver.self] 76 | LVThemeKitConfig.config = conf 77 | ``` 78 | 79 | ## 扩展 80 | - 扩展类,参考`UIView`/`CALayer` 81 | - 扩展属性 82 | - 支持 `KVC`,参考`UIView.backgroundColor` 83 | - 不支持 `KVC`,参考`UIButton.setTitle:forState:` 84 | - 替换已有的类,比如`UIView.viewTK`,可以新加一个属性`UIView.x_viewTK` 85 | 86 | ## 例子 87 | - `LVThemeKit/ObjC` 88 | - [MultiThemesDemo](https://github.com/lvpengwei/LVThemeKit/tree/master/Examples/MultiThemesDemo "MultiThemesDemo"): 多区域多主题,`OC`项目 89 | - [DayNightDemo](https://github.com/lvpengwei/LVThemeKit/tree/master/Examples/DayNightDemo "DayNightDemo"): 单主题(黑夜模式),`Swift`项目 90 | - `LVThemeKit/Swift` 91 | - [DayNightDemoSwift](https://github.com/lvpengwei/LVThemeKit/tree/master/Examples/DayNightDemoSwift "DayNightDemoSwift"):单主题(黑夜模式),`Swift`项目 92 | 93 | ## 安装 94 | ### Cocoapods 95 | `pod 'LVThemeKit/ObjC'` 96 | 97 | 或者 98 | 99 | `pod 'LVThemeKit/Swift'` 100 | 101 | ## 类图 102 | ![](https://i.loli.net/2019/04/17/5cb6d05c376eb.jpg) -------------------------------------------------------------------------------- /Source/Objc/CALayer+LVThemeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+LVThemeKit.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import 9 | #import "LVThemeKit.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class LVThemeColor; 14 | @interface CALayerTheme : LVThemeObject 15 | @property (nonatomic, strong) LVThemeColor * _Nullable backgroundColor; 16 | @property (nonatomic, strong) LVThemeColor * _Nullable borderColor; 17 | @end 18 | @interface CALayerThemeKit : LVThemeKit 19 | @end 20 | @interface CALayer (LVThemeKit) 21 | @property (nonatomic, strong, readonly) CALayerThemeKit *layerTK; 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /Source/Objc/CALayer+LVThemeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+LVThemeKit.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import "CALayer+LVThemeKit.h" 9 | #import 10 | 11 | @implementation CALayerTheme 12 | + (NSArray *)keyPaths { 13 | return @[@"backgroundColor", @"borderColor"]; 14 | } 15 | @end 16 | @implementation CALayerThemeKit 17 | + (Class)tClass { 18 | return [CALayerTheme class]; 19 | } 20 | @end 21 | @implementation CALayer (LVThemeKit) 22 | - (CALayerThemeKit *)layerTK { 23 | CALayerThemeKit *_layerTK = objc_getAssociatedObject(self, _cmd); 24 | if (_layerTK == nil) { 25 | _layerTK = [CALayerThemeKit instanceWithView:self]; 26 | objc_setAssociatedObject(self, _cmd, _layerTK, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 27 | } 28 | return _layerTK; 29 | } 30 | @end 31 | -------------------------------------------------------------------------------- /Source/Objc/CAShapeLayer+LVThemeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // CAShapeLayer+LVThemeKit.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @class LVThemeColor; 13 | @interface CAShapeLayerTheme : LVThemeObject 14 | @property (nonatomic, strong) LVThemeColor * _Nullable fillColor; 15 | @end 16 | @interface CAShapeLayerThemeKit : LVThemeKit 17 | @end 18 | @interface CAShapeLayer (LVThemeKit) 19 | @property (nonatomic, strong, readonly) CAShapeLayerThemeKit *shapeTK; 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Source/Objc/CAShapeLayer+LVThemeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // CAShapeLayer+LVThemeKit.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import "CAShapeLayer+LVThemeKit.h" 9 | #import 10 | 11 | @implementation CAShapeLayerTheme 12 | + (NSArray *)keyPaths { 13 | return @[@"fillColor"]; 14 | } 15 | @end 16 | @implementation CAShapeLayerThemeKit 17 | + (Class)tClass { 18 | return [CAShapeLayerTheme class]; 19 | } 20 | @end 21 | @implementation CAShapeLayer (LVThemeKit) 22 | - (CAShapeLayerThemeKit *)shapeTK { 23 | CAShapeLayerThemeKit *_viewTK = objc_getAssociatedObject(self, _cmd); 24 | if (_viewTK == nil) { 25 | _viewTK = [CAShapeLayerThemeKit instanceWithView:self]; 26 | objc_setAssociatedObject(self, _cmd, _viewTK, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 27 | } 28 | return _viewTK; 29 | } 30 | @end 31 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeAttributedString.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeAttributedString.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import "LVThemeResource.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface LVThemeAttributedString : LVThemeResource 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeAttributedString.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeAttributedString.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import "LVThemeAttributedString.h" 9 | 10 | @implementation LVThemeAttributedString 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeColor.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIKit/UIKit.h" 11 | #import "LVThemeResource.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface LVThemeColor : LVThemeResource 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeColor.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeColor.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeColor.h" 10 | 11 | @implementation LVThemeColor 12 | @end 13 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeImage.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIKit/UIKit.h" 11 | #import "LVThemeResource.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface LVThemeImage : LVThemeResource 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeImage.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeImage.h" 10 | 11 | @implementation LVThemeImage 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeKit.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class LVThemeKit, LVThemeObject, LVThemeResource; 14 | typedef void(^LVThemeKitCompletion)(void); 15 | @protocol LVThemeKitObserverGenerator 16 | - (void)lvThemeKitObserverGeneratorCompletion:(LVThemeKitCompletion)completion; 17 | @end 18 | 19 | typedef void(^LVThemeKitApplyPropertyCompletion)(LVThemeObject *theme); 20 | typedef void(^LVThemeKitApplyPropertyBlock)(LVThemeKit *tk, NSString *key, LVThemeKitApplyPropertyCompletion completion); 21 | @interface LVThemeKitConfig : NSObject 22 | // 生成皮肤监听的数组 23 | @property (nonatomic, copy) NSArray> *generators; 24 | // 自定义皮肤的应用顺序,默认是数组顺序。如果是多区域主题可以用到这个字段。 25 | @property (nonatomic, copy) LVThemeKitApplyPropertyBlock _Nullable applyProperty; 26 | @end 27 | 28 | @protocol LVThemeObjectProtocol 29 | - (void)themeObject:(LVThemeObject *)object property:(NSString *)key valueChanged:(LVThemeResource *)res; 30 | @end 31 | 32 | @interface LVThemeObject : NSObject 33 | @property (nonatomic, weak, readonly) id tk; 34 | - (instancetype)initWithTK:(id)tk; 35 | - (BOOL)hasValueForKey:(NSString *)key; 36 | + (NSArray *)keyPaths; 37 | @end 38 | 39 | @interface LVThemeKit : NSObject 40 | // 不可以为空 41 | @property (class, nonatomic, strong) LVThemeKitConfig *config; 42 | // 按照监听数组的顺序生成 T 实例 43 | @property (nonatomic, strong, readonly) NSArray *themes; 44 | // 数组中的第一个元素 45 | @property (nonatomic, strong, readonly) T theme; 46 | @property (nonatomic, weak, readonly) V view; 47 | + (instancetype)instanceWithView:(V)view; 48 | + (Class)tClass; 49 | - (void)apply:(T)object key:(NSString *)key; 50 | @end 51 | 52 | NS_ASSUME_NONNULL_END 53 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeKit.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeKit.h" 10 | #import "LVThemeResource.h" 11 | #import "NSString+LVThemeKit.h" 12 | 13 | @implementation LVThemeKitConfig 14 | @end 15 | 16 | @interface LVThemeObject () 17 | @property (nonatomic, weak, readwrite) id tk; 18 | @end 19 | @implementation LVThemeObject 20 | - (instancetype)initWithTK:(id)tk { 21 | self = [super init]; 22 | if (self) { 23 | self.tk = tk; 24 | [self addObservers]; 25 | } 26 | return self; 27 | } 28 | + (NSArray *)keyPaths { 29 | return @[]; 30 | } 31 | - (void)addObservers { 32 | for (NSString *key in [self class].keyPaths) { 33 | [self addObserverForKeyPath:key]; 34 | } 35 | } 36 | - (BOOL)hasValueForKey:(NSString *)key { 37 | return [self valueForKey:key] != nil; 38 | } 39 | - (void)addObserverForKeyPath:(NSString *)key { 40 | if ([key isUndefined]) return; 41 | [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil]; 42 | } 43 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 44 | [self.tk themeObject:self property:keyPath valueChanged:[self valueForKey:keyPath]]; 45 | } 46 | - (void)dealloc { 47 | for (NSString *key in [self class].keyPaths) { 48 | if ([key isUndefined]) continue; 49 | [self removeObserver:self forKeyPath:key]; 50 | } 51 | } 52 | @end 53 | 54 | @interface LVThemeKit () 55 | @property (nonatomic, strong, readwrite) NSArray *themes; 56 | @property (nonatomic, weak, readwrite) V view; 57 | @property (nonatomic, strong) NSMutableDictionary *observers; 58 | @property (nonatomic, weak) id readerObserver; 59 | @property (nonatomic, weak) id appObserver; 60 | @property (nonatomic, weak) id qdObserver; 61 | @end 62 | @implementation LVThemeKit 63 | static LVThemeKitConfig *_config = nil; 64 | + (void)setConfig:(LVThemeKitConfig *)config { 65 | if (_config != config) { 66 | _config = config; 67 | } 68 | } 69 | + (LVThemeKitConfig *)config { 70 | return _config; 71 | } 72 | + (Class)tClass { 73 | @throw [[NSException alloc] initWithName:@"主动异常" reason:@"子类实现" userInfo:nil]; 74 | } 75 | - (LVThemeObject *)theme { 76 | return self.themes[0]; 77 | } 78 | + (instancetype)instanceWithView:(id)view { 79 | return [[self alloc] initWithView:view tClass:[self tClass]]; 80 | } 81 | - (instancetype)initWithView:(id)view tClass:(Class)clz { 82 | self = [super init]; 83 | if (self) { 84 | self.view = view; 85 | NSAssert(LVThemeKit.config != nil, @"LVThemeKit.config 不能为空"); 86 | NSAssert(LVThemeKit.config.generators.count != 0, @"LVThemeKit.config.generators 不能为空"); 87 | NSMutableArray *themes = @[].mutableCopy; 88 | for (int i = 0; i < LVThemeKit.config.generators.count; i++) { 89 | [themes addObject:[[clz alloc] initWithTK:self]]; 90 | } 91 | self.themes = themes.copy; 92 | self.observers = @{}.mutableCopy; 93 | } 94 | return self; 95 | } 96 | - (void)themeObject:(LVThemeObject *)object property:(NSString *)key valueChanged:(LVThemeResource *)res { 97 | [self setupThemeObserve:object property:key valueChanged:res]; 98 | [self applyProperty:key]; 99 | } 100 | - (void)applyProperty:(NSString *)key { 101 | if (LVThemeKit.config.applyProperty) { 102 | __weak typeof(self) weakSelf = self; 103 | LVThemeKit.config.applyProperty(self, key, ^(LVThemeObject * _Nonnull theme) { 104 | __strong typeof(weakSelf) strongSelf = weakSelf; 105 | if (!strongSelf) return; 106 | [strongSelf apply:theme key:key]; 107 | }); 108 | return; 109 | } 110 | for (LVThemeObject *theme in self.themes) { 111 | if ([theme hasValueForKey:key]) { 112 | [self apply:theme key:key]; 113 | return; 114 | } 115 | } 116 | } 117 | - (void)setupThemeObserve:(LVThemeObject *)object property:(NSString *)key valueChanged:(LVThemeResource *)res { 118 | NSInteger index = -1; 119 | for (id t in self.themes) { 120 | index += 1; 121 | if (object == t) break; 122 | } 123 | if (index < 0 || index >= self.themes.count) return; 124 | if (self.observers[@(index)]) return; 125 | __weak typeof(self) weakSelf = self; 126 | Class clz = LVThemeKit.config.generators[index]; 127 | id generator = [[clz alloc] init]; 128 | [generator lvThemeKitObserverGeneratorCompletion:(^{ 129 | __strong typeof(weakSelf) strongSelf = weakSelf; 130 | if (!strongSelf) return; 131 | [strongSelf apply]; 132 | })]; 133 | self.observers[@(index)] = generator; 134 | } 135 | - (void)apply:(LVThemeObject *)object key:(NSString *)key { 136 | if ([self.view isKindOfClass:[CALayer class]]) { 137 | id resValue = ((LVThemeResource *)[object valueForKey:key]).resValue; 138 | if ([resValue isKindOfClass:[UIColor class]]) { 139 | [(CALayer *)self.view setValue:(id)((UIColor *)resValue).CGColor forKey:key]; 140 | } 141 | } else { 142 | [self.view setValue:((LVThemeResource *)[object valueForKey:key]).resValue forKey:key]; 143 | } 144 | } 145 | - (void)apply { 146 | for (NSString *key in [[[self class] tClass] keyPaths]) { 147 | [self applyProperty:key]; 148 | } 149 | } 150 | @end 151 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeResource.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface LVThemeResource : NSObject 14 | @property (nonatomic, readonly) T _Nullable resValue; 15 | + (instancetype)instance:(T _Nullable (^)(void))block; 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeResource.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeResource.h" 10 | 11 | @interface LVThemeResource () 12 | @property (nonatomic, copy) T (^block)(void); 13 | @end 14 | @implementation LVThemeResource 15 | + (instancetype)instance:(id (^)(void))block { 16 | return [[self alloc] initWithBlock:block]; 17 | } 18 | - (instancetype)initWithBlock:(id (^)(void))block { 19 | self = [super init]; 20 | if (self) { 21 | self.block = block; 22 | } 23 | return self; 24 | } 25 | - (id)resValue { 26 | return self.block(); 27 | } 28 | @end 29 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeString.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeString.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LVThemeResource.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface LVThemeString : LVThemeResource 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Source/Objc/LVThemeString.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeString.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "LVThemeString.h" 10 | 11 | @implementation LVThemeString 12 | @end 13 | -------------------------------------------------------------------------------- /Source/Objc/NSString+LVThemeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+LVThemeKit.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/13. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface NSString (LVThemeKit) 13 | - (NSString *)undefined; 14 | - (BOOL)isUndefined; 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Source/Objc/NSString+LVThemeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+LVThemeKit.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/13. 6 | // 7 | 8 | #import "NSString+LVThemeKit.h" 9 | 10 | NSString * const PREFIX_UNDEFINED = @"undefined"; 11 | @implementation NSString (LVThemeKit) 12 | - (NSString *)undefined { 13 | return [NSString stringWithFormat:@"%@_%@", PREFIX_UNDEFINED, self]; 14 | } 15 | - (BOOL)isUndefined { 16 | return [self hasPrefix:PREFIX_UNDEFINED]; 17 | } 18 | @end 19 | -------------------------------------------------------------------------------- /Source/Objc/UIButton+LVThemeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+LVThemeKit.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LVThemeKit.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @class LVThemeColor, LVThemeString, LVThemeAttributedString, LVThemeImage; 15 | @interface UIButtonTheme : LVThemeObject 16 | - (void)setTitleColor:(LVThemeColor * _Nullable)titleColor forState:(UIControlState)state; 17 | - (void)setTitle:(LVThemeString * _Nullable)title forState:(UIControlState)state; 18 | - (void)setImage:(LVThemeImage * _Nullable)image forState:(UIControlState)state; 19 | - (void)setAttributedTitle:(LVThemeAttributedString * _Nullable)title forState:(UIControlState)state; 20 | @end 21 | @interface UIButtonThemeKit : LVThemeKit 22 | @end 23 | @interface UIButton (LVThemeKit) 24 | @property (nonatomic, strong, readonly) UIButtonThemeKit *buttonTK; 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /Source/Objc/UIButton+LVThemeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+LVThemeKit.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "UIButton+LVThemeKit.h" 10 | #import "LVThemeColor.h" 11 | #import 12 | #import "LVThemeString.h" 13 | #import "LVThemeAttributedString.h" 14 | #import "NSString+LVThemeKit.h" 15 | 16 | @interface UIButtonTheme () 17 | @property (nonatomic, strong) NSMutableDictionary *titleColorDict; 18 | @property (nonatomic, strong) NSMutableDictionary *titleDict; 19 | @property (nonatomic, strong) NSMutableDictionary *imageDict; 20 | @property (nonatomic, strong) NSMutableDictionary *attributedTitleDict; 21 | @end 22 | @implementation UIButtonTheme 23 | - (instancetype)initWithTK:(id)tk { 24 | self = [super initWithTK:tk]; 25 | if (self) { 26 | self.titleColorDict = @{}.mutableCopy; 27 | self.titleDict = @{}.mutableCopy; 28 | self.imageDict = @{}.mutableCopy; 29 | self.attributedTitleDict = @{}.mutableCopy; 30 | } 31 | return self; 32 | } 33 | + (NSString *)titleColor { 34 | return [@"titleColor" undefined]; 35 | } 36 | + (NSString *)title { 37 | return [@"title" undefined]; 38 | } 39 | + (NSString *)image { 40 | return [@"image" undefined]; 41 | } 42 | + (NSString *)attributedTitle { 43 | return [@"attributedTitle" undefined]; 44 | } 45 | + (NSArray *)keyPaths { 46 | return @[[self title], [self titleColor], [self image], [self attributedTitle]]; 47 | } 48 | - (BOOL)hasValueForKey:(NSString *)key { 49 | if ([key isEqualToString:[[self class] titleColor]]) { 50 | return self.titleColorDict.count > 0; 51 | } else if ([key isEqualToString:[[self class] title]]) { 52 | return self.titleDict.count > 0; 53 | } else if ([key isEqualToString:[[self class] image]]) { 54 | return self.imageDict.count > 0; 55 | } else if ([key isEqualToString:[[self class] attributedTitle]]) { 56 | return self.attributedTitleDict.count > 0; 57 | } 58 | return NO; 59 | } 60 | - (void)setTitleColor:(LVThemeColor * _Nullable)titleColor forState:(UIControlState)state { 61 | [self.titleColorDict setObject:titleColor forKey:@(state)]; 62 | [self.tk themeObject:self property:[[self class] titleColor] valueChanged:titleColor]; 63 | } 64 | - (void)setTitle:(LVThemeString * _Nullable)title forState:(UIControlState)state { 65 | [self.titleDict setObject:title forKey:@(state)]; 66 | [self.tk themeObject:self property:[[self class] title] valueChanged:title]; 67 | } 68 | - (void)setImage:(LVThemeImage * _Nullable)image forState:(UIControlState)state { 69 | [self.imageDict setObject:image forKey:@(state)]; 70 | [self.tk themeObject:self property:[[self class] image] valueChanged:image]; 71 | } 72 | - (void)setAttributedTitle:(LVThemeAttributedString * _Nullable)title forState:(UIControlState)state { 73 | [self.attributedTitleDict setObject:title forKey:@(state)]; 74 | [self.tk themeObject:self property:[[self class] attributedTitle] valueChanged:title]; 75 | } 76 | @end 77 | @implementation UIButtonThemeKit 78 | + (Class)tClass { 79 | return [UIButtonTheme class]; 80 | } 81 | - (void)apply:(LVThemeObject *)object key:(NSString *)key { 82 | UIButtonTheme *theme = (UIButtonTheme *)object; 83 | if ([key isEqualToString:[[theme class] attributedTitle]]) { 84 | for (NSNumber *key in theme.attributedTitleDict) { 85 | LVThemeAttributedString *title = theme.attributedTitleDict[key]; 86 | [self.view setAttributedTitle:title.resValue forState:key.unsignedIntegerValue]; 87 | } 88 | } else if ([key isEqualToString:[[theme class] title]]) { 89 | for (NSNumber *key in theme.titleDict) { 90 | LVThemeString *title = theme.titleDict[key]; 91 | [self.view setTitle:title.resValue forState:key.unsignedIntegerValue]; 92 | } 93 | } else if ([key isEqualToString:[[theme class] image]]) { 94 | for (NSNumber *key in theme.imageDict) { 95 | LVThemeImage *image = theme.imageDict[key]; 96 | [self.view setImage:image.resValue forState:key.unsignedIntegerValue]; 97 | } 98 | } else if ([key isEqualToString:[[theme class] titleColor]]) { 99 | for (NSNumber *key in theme.titleColorDict) { 100 | LVThemeColor *color = theme.titleColorDict[key]; 101 | [self.view setTitleColor:color.resValue forState:key.unsignedIntegerValue]; 102 | } 103 | } 104 | } 105 | @end 106 | @implementation UIButton (LVThemeKit) 107 | - (UIButtonThemeKit *)buttonTK { 108 | UIButtonThemeKit *_viewTK = objc_getAssociatedObject(self, _cmd); 109 | if (_viewTK == nil) { 110 | _viewTK = [UIButtonThemeKit instanceWithView:self]; 111 | objc_setAssociatedObject(self, _cmd, _viewTK, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 112 | } 113 | return _viewTK; 114 | } 115 | @end 116 | -------------------------------------------------------------------------------- /Source/Objc/UIImageView+LVThemeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+LVThemeKit.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @class LVThemeImage; 13 | @interface UIImageViewTheme : LVThemeObject 14 | @property (nonatomic, strong) LVThemeImage * _Nullable image; 15 | @end 16 | @interface UIImageViewThemeKit : LVThemeKit 17 | @end 18 | @interface UIImageView (LVThemeKit) 19 | @property (nonatomic, strong, readonly) UIImageViewThemeKit *imageTK; 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Source/Objc/UIImageView+LVThemeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+LVThemeKit.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import "UIImageView+LVThemeKit.h" 9 | #import 10 | 11 | @implementation UIImageViewTheme 12 | + (NSArray *)keyPaths { 13 | return @[@"image"]; 14 | } 15 | @end 16 | @implementation UIImageViewThemeKit 17 | + (Class)tClass { 18 | return [UIImageViewTheme class]; 19 | } 20 | @end 21 | @implementation UIImageView (LVThemeKit) 22 | - (UIImageViewThemeKit *)imageTK { 23 | UIImageViewThemeKit *_imageTK = objc_getAssociatedObject(self, _cmd); 24 | if (_imageTK == nil) { 25 | _imageTK = [UIImageViewThemeKit instanceWithView:self]; 26 | objc_setAssociatedObject(self, _cmd, _imageTK, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 27 | } 28 | return _imageTK; 29 | } 30 | @end 31 | -------------------------------------------------------------------------------- /Source/Objc/UILabel+LVThemeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+LVThemeKit.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LVThemeKit.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @class LVThemeColor, LVThemeAttributedString; 15 | @interface UILabelTheme : LVThemeObject 16 | @property (nonatomic, strong) LVThemeColor * _Nullable textColor; 17 | @property (nonatomic, strong) LVThemeAttributedString * _Nullable attributedText; 18 | @end 19 | @interface UILabelThemeKit : LVThemeKit 20 | @end 21 | @interface UILabel (LVThemeKit) 22 | @property (nonatomic, strong, readonly) UILabelThemeKit *labelTK; 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /Source/Objc/UILabel+LVThemeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+LVThemeKit.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "UILabel+LVThemeKit.h" 10 | #import 11 | 12 | @implementation UILabelTheme 13 | + (NSArray *)keyPaths { 14 | return @[@"textColor", @"attributedText"]; 15 | } 16 | @end 17 | @implementation UILabelThemeKit 18 | + (Class)tClass { 19 | return [UILabelTheme class]; 20 | } 21 | @end 22 | @implementation UILabel (LVThemeKit) 23 | - (UILabelThemeKit *)labelTK { 24 | UILabelThemeKit *_viewTK = objc_getAssociatedObject(self, _cmd); 25 | if (_viewTK == nil) { 26 | _viewTK = [UILabelThemeKit instanceWithView:self]; 27 | objc_setAssociatedObject(self, _cmd, _viewTK, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 28 | } 29 | return _viewTK; 30 | } 31 | @end 32 | -------------------------------------------------------------------------------- /Source/Objc/UISlider+LVThemeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // UISlider+LVThemeKit.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @class LVThemeColor; 13 | @interface UISliderTheme : LVThemeObject 14 | @property (nonatomic, strong) LVThemeColor * _Nullable minimumTrackTintColor; 15 | @property (nonatomic, strong) LVThemeColor * _Nullable maximumTrackTintColor; 16 | @end 17 | @interface UISliderThemeKit : LVThemeKit 18 | @end 19 | @interface UISlider (LVThemeKit) 20 | @property (nonatomic, strong, readonly) UISliderThemeKit *sliderTK; 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /Source/Objc/UISlider+LVThemeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // UISlider+LVThemeKit.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/8. 6 | // 7 | 8 | #import "UISlider+LVThemeKit.h" 9 | #import 10 | 11 | @implementation UISliderTheme 12 | + (NSArray *)keyPaths { 13 | return @[@"minimumTrackTintColor", @"maximumTrackTintColor"]; 14 | } 15 | @end 16 | @implementation UISliderThemeKit 17 | + (Class)tClass { 18 | return [UISliderTheme class]; 19 | } 20 | @end 21 | @implementation UISlider (LVThemeKit) 22 | - (UISliderThemeKit *)sliderTK { 23 | UISliderThemeKit *_sliderTK = objc_getAssociatedObject(self, _cmd); 24 | if (_sliderTK == nil) { 25 | _sliderTK = [UISliderThemeKit instanceWithView:self]; 26 | objc_setAssociatedObject(self, _cmd, _sliderTK, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 27 | } 28 | return _sliderTK; 29 | } 30 | @end 31 | -------------------------------------------------------------------------------- /Source/Objc/UIView+LVThemeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LVThemeKit.h 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LVThemeKit.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @class LVThemeColor; 15 | @interface UIViewTheme : LVThemeObject 16 | @property (nonatomic, strong) LVThemeColor * _Nullable backgroundColor; 17 | @property (nonatomic, strong) LVThemeColor * _Nullable tintColor; 18 | @end 19 | @interface UIViewThemeKit : LVThemeKit 20 | @end 21 | @interface UIView (LVThemeKit) 22 | @property (nonatomic, strong, readonly) UIViewThemeKit *viewTK; 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /Source/Objc/UIView+LVThemeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LVThemeKit.m 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/3/30. 6 | // Copyright © 2019 lvpengwei. All rights reserved. 7 | // 8 | 9 | #import "UIView+LVThemeKit.h" 10 | #import 11 | 12 | @implementation UIViewTheme 13 | + (NSArray *)keyPaths { 14 | return @[@"backgroundColor", @"tintColor"]; 15 | } 16 | @end 17 | @implementation UIViewThemeKit 18 | + (Class)tClass { 19 | return [UIViewTheme class]; 20 | } 21 | @end 22 | @implementation UIView (LVThemeKit) 23 | - (UIViewThemeKit *)viewTK { 24 | UIViewThemeKit *_viewTK = objc_getAssociatedObject(self, _cmd); 25 | if (_viewTK == nil) { 26 | _viewTK = [UIViewThemeKit instanceWithView:self]; 27 | objc_setAssociatedObject(self, _cmd, _viewTK, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 28 | } 29 | return _viewTK; 30 | } 31 | @end 32 | -------------------------------------------------------------------------------- /Source/Swift/CALayerTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CALayerTheme.swift 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/12. 6 | // 7 | 8 | import Foundation 9 | 10 | public class CALayerTheme: LVThemeObject { 11 | @objc public dynamic var backgroundColor: LVThemeColor? 12 | @objc public dynamic var borderColor: LVThemeColor? 13 | class override func keyPaths() -> [String] { 14 | return ["backgroundColor", "borderColor"] 15 | } 16 | } 17 | public class CALayerThemeKit: LVThemeKit { } 18 | private var layerTKKey: UInt8 = 0 19 | extension CALayer { 20 | public var layerTK: CALayerThemeKit { 21 | var _layerTK = objc_getAssociatedObject(self, &layerTKKey) as? CALayerThemeKit 22 | if _layerTK == nil { 23 | _layerTK = CALayerThemeKit(with: self) 24 | objc_setAssociatedObject(self, &layerTKKey, _layerTK, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 25 | } 26 | return _layerTK! 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/Swift/CAShapeLayerTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CAShapeLayerTheme.swift 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/12. 6 | // 7 | 8 | import Foundation 9 | 10 | public class CAShapeLayerTheme: LVThemeObject { 11 | @objc public dynamic var fillColor: LVThemeColor? 12 | class override func keyPaths() -> [String] { 13 | return ["fillColor"] 14 | } 15 | } 16 | public class CAShapeLayerThemeKit: LVThemeKit { } 17 | private var shapeTKKey: UInt8 = 0 18 | extension CAShapeLayer { 19 | public var shapeTK: CAShapeLayerThemeKit { 20 | var _shapeTK = objc_getAssociatedObject(self, &shapeTKKey) as? CAShapeLayerThemeKit 21 | if _shapeTK == nil { 22 | _shapeTK = CAShapeLayerThemeKit(with: self) 23 | objc_setAssociatedObject(self, &shapeTKKey, _shapeTK, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 24 | } 25 | return _shapeTK! 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Swift/LVThemeKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeKit.swift 3 | // Pods-DayNightDemoSwift 4 | // 5 | // Created by lvpengwei on 2019/4/10. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol LVThemeObjectProtocol: class { 11 | func themeObject(_ obj: LVThemeObject, property: String, changed value: LVThemeResource?) 12 | } 13 | public class LVThemeObject: NSObject { 14 | private(set) weak var tk: LVThemeObjectProtocol! 15 | required init(with tk: LVThemeObjectProtocol) { 16 | super.init() 17 | self.tk = tk 18 | addObservers() 19 | } 20 | func hasValue(for key: String) -> Bool { 21 | return value(forKey: key) != nil 22 | } 23 | class func keyPaths() -> [String] { 24 | return [] 25 | } 26 | private func addObservers() { 27 | type(of: self).keyPaths().compactMap({ $0.isUndefined ? nil : $0 }).forEach({ addObserver(for: $0) }) 28 | } 29 | private func addObserver(for keyPath: String) { 30 | addObserver(self, forKeyPath: keyPath, options: .new, context: nil) 31 | } 32 | override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 33 | guard let keyPath = keyPath, let value = value(forKey: keyPath) as? LVThemeResource else { return } 34 | tk.themeObject(self, property: keyPath, changed: value) 35 | } 36 | deinit { 37 | type(of: self).keyPaths().compactMap({ $0.isUndefined ? nil : $0 }).forEach({ removeObserver(self, forKeyPath: $0) }) 38 | } 39 | } 40 | 41 | public protocol LVThemeKitObserverGenerator: class { 42 | init() 43 | func lvThemeKitObserverGenerator(completion: @escaping () -> Void) 44 | } 45 | 46 | public class LVThemeKitConfig { 47 | public static var config: LVThemeKitConfig? 48 | public var generators: [LVThemeKitObserverGenerator.Type] = [] 49 | public var applyProperty: ((LVThemeKit, String, (LVThemeObject) -> Void) -> Void)? 50 | public init() { } 51 | } 52 | public class LVThemeKit: LVThemeObjectProtocol { 53 | private(set) weak var view: V! 54 | public private(set) var themes: [T] 55 | public var theme: T { return themes[0] } 56 | private var observers: [Int: LVThemeKitObserverGenerator] = [:] 57 | init(with view: V) { 58 | self.view = view 59 | assert(LVThemeKitConfig.config != nil, "LVThemeKit.config 不能为空") 60 | let conf = LVThemeKitConfig.config! 61 | assert(!conf.generators.isEmpty, "LVThemeKit.config.generators 不能为空") 62 | self.themes = [] 63 | conf.generators.forEach({ _ in 64 | self.themes.append(T(with: self)) 65 | }) 66 | } 67 | func themeObject(_ obj: LVThemeObject, property: String, changed value: LVThemeResource?) { 68 | setup(themeObject: obj, property: property, changed: value) 69 | apply(property: property) 70 | } 71 | private func setup(themeObject: LVThemeObject, property: String, changed value: LVThemeResource?) { 72 | guard let index = themes.firstIndex(where: { $0 === themeObject }) else { return } 73 | guard observers[index] == nil else { return } 74 | let clz = LVThemeKitConfig.config!.generators[index] 75 | let generator = clz.init() 76 | generator.lvThemeKitObserverGenerator { [weak self] in 77 | guard let this = self else { return } 78 | this.apply() 79 | } 80 | observers[index] = generator 81 | } 82 | private func apply(property key: String) { 83 | if let applyProperty = LVThemeKitConfig.config?.applyProperty, let s = self as? LVThemeKit { 84 | applyProperty(s, key, { [weak self] (theme) in 85 | guard let this = self else { return } 86 | guard let t = theme as? T else { return } 87 | this.apply(object: t, key: key) 88 | }) 89 | return 90 | } 91 | for t in themes { 92 | if t.hasValue(for: key) { 93 | apply(object: t, key: key) 94 | break 95 | } 96 | } 97 | } 98 | func apply(object: T, key: String) { 99 | guard let v = object.value(forKey: key) as? LVThemeResource else { return } 100 | if view is CALayer { 101 | if let resValue = v.resValue as? UIColor { 102 | view.setValue(resValue.cgColor, forKey: key) 103 | } 104 | } else { 105 | view.setValue(v.resValue, forKey: key) 106 | } 107 | } 108 | private func apply() { 109 | T.keyPaths().forEach({ apply(property: $0) }) 110 | } 111 | } 112 | 113 | private let PREFIX_UNDEINED = "undefined_" 114 | extension String { 115 | var undefined: String { 116 | return PREFIX_UNDEINED + self 117 | } 118 | var isUndefined: Bool { 119 | return hasPrefix(PREFIX_UNDEINED) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Source/Swift/LVThemeResource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LVThemeResource.swift 3 | // Pods-DayNightDemoSwift 4 | // 5 | // Created by lvpengwei on 2019/4/10. 6 | // 7 | 8 | import Foundation 9 | 10 | public class LVThemeResource: NSObject { 11 | public var resValue: NSObject? { 12 | return self.block() 13 | } 14 | private var block: (() -> NSObject?) 15 | public required init(with block: @escaping (() -> NSObject?)) { 16 | self.block = block 17 | } 18 | } 19 | 20 | public class LVThemeColor: LVThemeResource { } 21 | 22 | public class LVThemeImage: LVThemeResource { } 23 | 24 | public class LVThemeAttributedString: LVThemeResource { } 25 | 26 | public class LVThemeString: LVThemeResource { } 27 | -------------------------------------------------------------------------------- /Source/Swift/UIButtonTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButtonTheme.swift 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/12. 6 | // 7 | 8 | import Foundation 9 | 10 | private let TITLE = "title".undefined 11 | private let TITLECOLOR = "titleColor".undefined 12 | private let IMAGE = "image".undefined 13 | private let ATTRIBUTEDTITLE = "attributedTitle".undefined 14 | public class UIButtonTheme: LVThemeObject { 15 | private(set) var titleDict: [UInt: LVThemeString] = [:] 16 | private(set) var titleColorDict: [UInt: LVThemeColor] = [:] 17 | private(set) var imageDict: [UInt: LVThemeImage] = [:] 18 | private(set) var attributedTitleDict: [UInt: LVThemeAttributedString] = [:] 19 | public func setTitle(_ title: LVThemeString?, for state: UIControl.State) { 20 | titleDict[state.rawValue] = title 21 | tk.themeObject(self, property: TITLE, changed: title) 22 | } 23 | public func setTitleColor(_ color: LVThemeColor?, for state: UIControl.State) { 24 | titleColorDict[state.rawValue] = color 25 | tk.themeObject(self, property: TITLECOLOR, changed: color) 26 | } 27 | public func setImage(_ image: LVThemeImage?, for state: UIControl.State) { 28 | imageDict[state.rawValue] = image 29 | tk.themeObject(self, property: IMAGE, changed: image) 30 | } 31 | public func setAttributedTitle(_ title: LVThemeAttributedString?, for state: UIControl.State) { 32 | attributedTitleDict[state.rawValue] = title 33 | tk.themeObject(self, property: ATTRIBUTEDTITLE, changed: title) 34 | } 35 | class override func keyPaths() -> [String] { 36 | return [TITLE, TITLECOLOR, IMAGE, ATTRIBUTEDTITLE] 37 | } 38 | override func hasValue(for key: String) -> Bool { 39 | switch key { 40 | case TITLE: 41 | return !titleDict.isEmpty 42 | case TITLECOLOR: 43 | return !titleColorDict.isEmpty 44 | case IMAGE: 45 | return !imageDict.isEmpty 46 | case ATTRIBUTEDTITLE: 47 | return !attributedTitleDict.isEmpty 48 | default: 49 | return false 50 | } 51 | } 52 | } 53 | public class UIButtonThemeKit: LVThemeKit { 54 | override func apply(object: UIButtonTheme, key: String) { 55 | switch key { 56 | case TITLE: 57 | object.titleDict.forEach({ 58 | guard let title = $1.resValue as? String else { return } 59 | view.setTitle(title, for: UIControl.State(rawValue: $0)) 60 | }) 61 | case TITLECOLOR: 62 | object.titleColorDict.forEach({ 63 | guard let color = $1.resValue as? UIColor else { return } 64 | view.setTitleColor(color, for: UIControl.State(rawValue: $0)) 65 | }) 66 | case IMAGE: 67 | object.imageDict.forEach({ 68 | guard let image = $1.resValue as? UIImage else { return } 69 | view.setImage(image, for: UIControl.State(rawValue: $0)) 70 | }) 71 | case ATTRIBUTEDTITLE: 72 | object.attributedTitleDict.forEach({ 73 | guard let title = $1.resValue as? NSAttributedString else { return } 74 | view.setAttributedTitle(title, for: UIControl.State(rawValue: $0)) 75 | }) 76 | default: 77 | break 78 | } 79 | } 80 | } 81 | private var buttonTKKey: UInt8 = 0 82 | extension UIButton { 83 | public var buttonTK: UIButtonThemeKit { 84 | var _buttonTK = objc_getAssociatedObject(self, &buttonTKKey) as? UIButtonThemeKit 85 | if _buttonTK == nil { 86 | _buttonTK = UIButtonThemeKit(with: self) 87 | objc_setAssociatedObject(self, &buttonTKKey, _buttonTK, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 88 | } 89 | return _buttonTK! 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Source/Swift/UIImageViewTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageViewTheme.swift 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/12. 6 | // 7 | 8 | import Foundation 9 | 10 | public class UIImageViewTheme: LVThemeObject { 11 | @objc public dynamic var image: LVThemeImage? 12 | class override func keyPaths() -> [String] { 13 | return ["image"] 14 | } 15 | } 16 | public class UIImageViewThemeKit: LVThemeKit { } 17 | private var imageTKKey: UInt8 = 0 18 | extension UIImageView { 19 | public var imageTK: UIImageViewThemeKit { 20 | var _imageTK = objc_getAssociatedObject(self, &imageTKKey) as? UIImageViewThemeKit 21 | if _imageTK == nil { 22 | _imageTK = UIImageViewThemeKit(with: self) 23 | objc_setAssociatedObject(self, &imageTKKey, _imageTK, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 24 | } 25 | return _imageTK! 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Swift/UILabelTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UILabelTheme.swift 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/12. 6 | // 7 | 8 | import Foundation 9 | 10 | public class UILabelTheme: LVThemeObject { 11 | @objc public dynamic var textColor: LVThemeColor? 12 | @objc public dynamic var attributedText: LVThemeAttributedString? 13 | class override func keyPaths() -> [String] { 14 | return ["textColor", "attributedText"] 15 | } 16 | } 17 | public class UILabelThemeKit: LVThemeKit { } 18 | private var labelTKKey: UInt8 = 0 19 | extension UILabel { 20 | public var labelTK: UILabelThemeKit { 21 | var _labelTK = objc_getAssociatedObject(self, &labelTKKey) as? UILabelThemeKit 22 | if _labelTK == nil { 23 | _labelTK = UILabelThemeKit(with: self) 24 | objc_setAssociatedObject(self, &labelTKKey, _labelTK, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 25 | } 26 | return _labelTK! 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/Swift/UISliderTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UISliderTheme.swift 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/12. 6 | // 7 | 8 | import Foundation 9 | 10 | public class UISliderTheme: LVThemeObject { 11 | @objc public dynamic var minimumTrackTintColor: LVThemeColor? 12 | @objc public dynamic var maximumTrackTintColor: LVThemeColor? 13 | class override func keyPaths() -> [String] { 14 | return ["minimumTrackTintColor", "maximumTrackTintColor"] 15 | } 16 | } 17 | public class UISliderThemeKit: LVThemeKit { } 18 | private var sliderTKKey: UInt8 = 0 19 | extension UISlider { 20 | public var sliderTK: UISliderThemeKit { 21 | var _sliderTK = objc_getAssociatedObject(self, &sliderTKKey) as? UISliderThemeKit 22 | if _sliderTK == nil { 23 | _sliderTK = UISliderThemeKit(with: self) 24 | objc_setAssociatedObject(self, &sliderTKKey, _sliderTK, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 25 | } 26 | return _sliderTK! 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/Swift/UIViewTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewTheme.swift 3 | // LVThemeKit 4 | // 5 | // Created by lvpengwei on 2019/4/10. 6 | // 7 | 8 | import Foundation 9 | 10 | public class UIViewTheme: LVThemeObject { 11 | @objc public dynamic var backgroundColor: LVThemeColor? 12 | class override func keyPaths() -> [String] { 13 | return ["backgroundColor"] 14 | } 15 | } 16 | public class UIViewThemeKit: LVThemeKit { } 17 | private var viewTKKey: UInt8 = 0 18 | extension UIView { 19 | public var viewTK: UIViewThemeKit { 20 | var _viewTK = objc_getAssociatedObject(self, &viewTKKey) as? UIViewThemeKit 21 | if _viewTK == nil { 22 | _viewTK = UIViewThemeKit(with: self) 23 | objc_setAssociatedObject(self, &viewTKKey, _viewTK, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 24 | } 25 | return _viewTK! 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /oneclick.sh: -------------------------------------------------------------------------------- 1 | cd Examples/DayNightDemo 2 | pod install 3 | cd ../../ 4 | cd Examples/MultiThemesDemo 5 | pod install 6 | cd ../../ 7 | cd Examples/DayNightDemoSwift 8 | pod install 9 | cd ../../ --------------------------------------------------------------------------------