├── README.md ├── ThemeKit ├── Assets.xcassets │ ├── Contents.json │ ├── first.imageset │ │ ├── first.pdf │ │ └── Contents.json │ ├── second.imageset │ │ ├── second.pdf │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Controls │ ├── AppSlider.swift │ ├── AppSwitch.swift │ ├── AppStepper.swift │ ├── AppSegmentedControl.swift │ ├── AppView.swift │ ├── AppButton.swift │ └── AppLabel.swift ├── AppDelegate.swift ├── MainViewController.swift ├── Extensions │ ├── UITableViewCell.swift │ ├── UIWindow.swift │ ├── With.swift │ └── UIView.swift ├── Styles │ ├── Themes │ │ ├── DarkTheme.swift │ │ ├── OceanTheme.swift │ │ └── LightTheme.swift │ └── Theme.swift ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard └── Info.plist ├── ThemeKit.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── LICENSE └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # Protocol-Oriented Themes for iOS Apps 2 | http://basememara.com/protocol-oriented-themes-for-ios-apps/ 3 | -------------------------------------------------------------------------------- /ThemeKit/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ThemeKit/Assets.xcassets/first.imageset/first.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basememara/ThemeKit/HEAD/ThemeKit/Assets.xcassets/first.imageset/first.pdf -------------------------------------------------------------------------------- /ThemeKit/Assets.xcassets/second.imageset/second.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basememara/ThemeKit/HEAD/ThemeKit/Assets.xcassets/second.imageset/second.pdf -------------------------------------------------------------------------------- /ThemeKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ThemeKit/Assets.xcassets/first.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "first.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /ThemeKit/Assets.xcassets/second.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "second.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /ThemeKit/Controls/AppSlider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppSlider.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-29. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AppSlider: UISlider { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ThemeKit/Controls/AppSwitch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppSwitch.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AppSwitch: UISwitch { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ThemeKit/Controls/AppStepper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppStepper.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-29. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AppStepper: UIStepper { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ThemeKit/Controls/AppSegmentedControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppSegmentedControl.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AppSegmentedControl: UISegmentedControl { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ThemeKit/Controls/AppView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppView.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AppView: UIView { 12 | 13 | } 14 | 15 | class AppSeparator: UIView { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /ThemeKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ThemeKit/Controls/AppButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppButton.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AppButton: UIButton { 12 | 13 | } 14 | 15 | class AppDangerButton: UIButton { 16 | 17 | } 18 | 19 | class AppImageButton: UIButton { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /ThemeKit/Controls/AppLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppLabel.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AppLabel: UILabel { 12 | 13 | } 14 | 15 | class AppHeadline: UILabel { 16 | 17 | } 18 | 19 | class AppSubhead: UILabel { 20 | 21 | } 22 | 23 | class AppFootnote: UILabel { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /ThemeKit/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-27. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | private let theme = DarkTheme() 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { 18 | theme.apply(for: application) 19 | return true 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /ThemeKit/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MainViewController: UIViewController { 12 | 13 | @IBAction func themeSegmentedControlChanged(_ sender: UISegmentedControl) { 14 | let theme: Theme 15 | 16 | switch sender.selectedSegmentIndex { 17 | case 1: theme = LightTheme() 18 | case 2: theme = OceanTheme() 19 | default: theme = DarkTheme() 20 | } 21 | 22 | theme.apply(for: UIApplication.shared) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ThemeKit/Extensions/UITableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UITableViewCell { 12 | 13 | /// The color of the cell when it is selected. 14 | @objc dynamic var selectionColor: UIColor? { 15 | get { return selectedBackgroundView?.backgroundColor } 16 | set { 17 | guard selectionStyle != .none else { return } 18 | selectedBackgroundView = UIView().with { 19 | $0.backgroundColor = newValue 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ThemeKit/Styles/Themes/DarkTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DarkTheme.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-27. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct DarkTheme: Theme { 12 | let tint: UIColor = .yellow 13 | let secondaryTint: UIColor = .green 14 | 15 | let backgroundColor: UIColor = .black 16 | let separatorColor: UIColor = .lightGray 17 | let selectionColor: UIColor = .init(red: 38/255, green: 38/255, blue: 40/255, alpha: 1) 18 | 19 | let labelColor: UIColor = .white 20 | let secondaryLabelColor: UIColor = .lightGray 21 | let subtleLabelColor: UIColor = .darkGray 22 | 23 | let barStyle: UIBarStyle = .black 24 | } 25 | -------------------------------------------------------------------------------- /ThemeKit/Styles/Themes/OceanTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OceanTheme.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-27. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct OceanTheme: Theme { 12 | let tint: UIColor = .blue 13 | let secondaryTint: UIColor = .orange 14 | 15 | let backgroundColor: UIColor = .cyan 16 | let separatorColor: UIColor = .lightGray 17 | let selectionColor: UIColor = .init(red: 38/255, green: 38/255, blue: 40/255, alpha: 1) 18 | 19 | let labelColor: UIColor = .magenta 20 | let secondaryLabelColor: UIColor = .lightGray 21 | let subtleLabelColor: UIColor = .darkGray 22 | 23 | let barStyle: UIBarStyle = .default 24 | } 25 | -------------------------------------------------------------------------------- /ThemeKit/Extensions/UIWindow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIWindow.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UIWindow { 12 | 13 | /// Unload all views and add back. 14 | /// Useful for applying `UIAppearance` changes to existing views. 15 | func reload() { 16 | subviews.forEach { view in 17 | view.removeFromSuperview() 18 | addSubview(view) 19 | } 20 | } 21 | } 22 | 23 | public extension Array where Element == UIWindow { 24 | 25 | /// Unload all views for each `UIWindow` and add back. 26 | /// Useful for applying `UIAppearance` changes to existing views. 27 | func reload() { 28 | forEach { $0.reload() } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ThemeKit/Extensions/With.swift: -------------------------------------------------------------------------------- 1 | // 2 | // With.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol With {} 12 | 13 | public extension With where Self: Any { 14 | 15 | /// Makes it available to set properties with closures just after initializing. 16 | /// 17 | /// let label = UILabel().with { 18 | /// $0.textAlignment = .center 19 | /// $0.textColor = UIColor.black 20 | /// $0.text = "Hello, World!" 21 | /// } 22 | @discardableResult 23 | func with(_ block: (Self) -> Void) -> Self { 24 | // https://github.com/devxoul/Then 25 | block(self) 26 | return self 27 | } 28 | } 29 | 30 | extension NSObject: With {} 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Basem Emara 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 | -------------------------------------------------------------------------------- /ThemeKit/Styles/Themes/LightTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LightTheme.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-27. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct LightTheme: Theme { 12 | let tint: UIColor = .blue 13 | let secondaryTint: UIColor = .orange 14 | 15 | let backgroundColor: UIColor = .white 16 | let separatorColor: UIColor = .lightGray 17 | let selectionColor: UIColor = UIColor(red: 236/255, green: 236/255, blue: 236/255, alpha: 1) 18 | 19 | let labelColor: UIColor = .black 20 | let secondaryLabelColor: UIColor = .darkGray 21 | let subtleLabelColor: UIColor = .lightGray 22 | 23 | let barStyle: UIBarStyle = .default 24 | } 25 | 26 | extension LightTheme { 27 | 28 | func extend() { 29 | UIImageView.appearance(whenContainedInInstancesOf: [UITableViewCell.self]).with { 30 | $0.borderColor = separatorColor 31 | $0.borderWidth = 1 32 | } 33 | 34 | UIImageView.appearance(whenContainedInInstancesOf: [UIButton.self, UITableViewCell.self]).with { 35 | $0.borderWidth = 0 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ThemeKit/Extensions/UIView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-28. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UIView { 12 | 13 | /// Border color of view; also inspectable from Storyboard. 14 | @IBInspectable var borderColor: UIColor? { 15 | get { 16 | guard let color = layer.borderColor else { return nil } 17 | return UIColor(cgColor: color) 18 | } 19 | 20 | set { 21 | guard let color = newValue else { 22 | layer.borderColor = nil 23 | return 24 | } 25 | 26 | layer.borderColor = color.cgColor 27 | } 28 | } 29 | 30 | /// Border width of view; also inspectable from Storyboard. 31 | @IBInspectable var borderWidth: CGFloat { 32 | get { return layer.borderWidth } 33 | set { layer.borderWidth = newValue } 34 | } 35 | 36 | /// Corner radius of view; also inspectable from Storyboard. 37 | @IBInspectable var cornerRadius: CGFloat { 38 | get { return layer.cornerRadius } 39 | 40 | set { 41 | layer.masksToBounds = true 42 | layer.cornerRadius = newValue 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /ThemeKit/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 | -------------------------------------------------------------------------------- /ThemeKit/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 | UIStatusBarTintParameters 32 | 33 | UINavigationBar 34 | 35 | Style 36 | UIBarStyleDefault 37 | Translucent 38 | 39 | 40 | 41 | UISupportedInterfaceOrientations 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | UISupportedInterfaceOrientations~ipad 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationPortraitUpsideDown 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /ThemeKit/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 | } -------------------------------------------------------------------------------- /ThemeKit/Styles/Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theme.swift 3 | // ThemeKit 4 | // 5 | // Created by Basem Emara on 2018-09-27. 6 | // Copyright © 2018 Basem Emara. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol Theme { 12 | var tint: UIColor { get } 13 | var secondaryTint: UIColor { get } 14 | 15 | var backgroundColor: UIColor { get } 16 | var separatorColor: UIColor { get } 17 | var selectionColor: UIColor { get } 18 | 19 | var labelColor: UIColor { get } 20 | var secondaryLabelColor: UIColor { get } 21 | var subtleLabelColor: UIColor { get } 22 | 23 | var barStyle: UIBarStyle { get } 24 | 25 | func apply(for application: UIApplication) 26 | func extend() 27 | } 28 | 29 | extension Theme { 30 | 31 | func apply(for application: UIApplication) { 32 | application.keyWindow?.tintColor = tint 33 | 34 | UITabBar.appearance().with { 35 | $0.barStyle = barStyle 36 | $0.tintColor = tint 37 | } 38 | 39 | UINavigationBar.appearance().with { 40 | $0.barStyle = barStyle 41 | $0.tintColor = tint 42 | $0.titleTextAttributes = [ 43 | .foregroundColor: labelColor 44 | ] 45 | 46 | if #available(iOS 11.0, *) { 47 | $0.largeTitleTextAttributes = [ 48 | .foregroundColor: labelColor 49 | ] 50 | } 51 | } 52 | 53 | UICollectionView.appearance().backgroundColor = backgroundColor 54 | 55 | UITableView.appearance().with { 56 | $0.backgroundColor = backgroundColor 57 | $0.separatorColor = separatorColor 58 | } 59 | 60 | UITableViewCell.appearance().with { 61 | $0.backgroundColor = .clear 62 | $0.selectionColor = selectionColor 63 | } 64 | 65 | UIView.appearance(whenContainedInInstancesOf: [UITableViewHeaderFooterView.self]) 66 | .backgroundColor = selectionColor 67 | 68 | UILabel.appearance(whenContainedInInstancesOf: [UITableViewHeaderFooterView.self]) 69 | .textColor = secondaryLabelColor 70 | 71 | AppLabel.appearance().textColor = labelColor 72 | AppHeadline.appearance().textColor = secondaryTint 73 | AppSubhead.appearance().textColor = secondaryLabelColor 74 | AppFootnote.appearance().textColor = subtleLabelColor 75 | 76 | AppButton.appearance().with { 77 | $0.setTitleColor(tint, for: .normal) 78 | $0.borderColor = tint 79 | $0.borderWidth = 1 80 | $0.cornerRadius = 3 81 | } 82 | 83 | AppDangerButton.appearance().with { 84 | $0.setTitleColor(backgroundColor, for: .normal) 85 | $0.backgroundColor = tint 86 | $0.cornerRadius = 3 87 | } 88 | 89 | AppSwitch.appearance().with { 90 | $0.tintColor = tint 91 | $0.onTintColor = tint 92 | } 93 | 94 | AppStepper.appearance().tintColor = tint 95 | 96 | AppSlider.appearance().tintColor = tint 97 | 98 | AppSegmentedControl.appearance().tintColor = tint 99 | 100 | AppView.appearance().backgroundColor = backgroundColor 101 | 102 | AppSeparator.appearance().with { 103 | $0.backgroundColor = separatorColor 104 | $0.alpha = 0.5 105 | } 106 | 107 | AppView.appearance(whenContainedInInstancesOf: [AppView.self]).with { 108 | $0.backgroundColor = selectionColor 109 | $0.cornerRadius = 10 110 | } 111 | 112 | // Style differently when inside a special container 113 | 114 | AppLabel.appearance(whenContainedInInstancesOf: [AppView.self, AppView.self]).textColor = subtleLabelColor 115 | AppHeadline.appearance(whenContainedInInstancesOf: [AppView.self, AppView.self]).textColor = secondaryLabelColor 116 | AppSubhead.appearance(whenContainedInInstancesOf: [AppView.self, AppView.self]).textColor = secondaryTint 117 | AppFootnote.appearance(whenContainedInInstancesOf: [AppView.self, AppView.self]).textColor = labelColor 118 | 119 | AppButton.appearance(whenContainedInInstancesOf: [AppView.self, AppView.self]).with { 120 | $0.setTitleColor(labelColor, for: .normal) 121 | $0.borderColor = labelColor 122 | } 123 | 124 | AppDangerButton.appearance(whenContainedInInstancesOf: [AppView.self, AppView.self]).with { 125 | $0.setTitleColor(subtleLabelColor, for: .normal) 126 | $0.backgroundColor = labelColor 127 | } 128 | 129 | AppSwitch.appearance(whenContainedInInstancesOf: [AppView.self, AppView.self]).with { 130 | $0.tintColor = secondaryTint 131 | $0.onTintColor = secondaryTint 132 | } 133 | 134 | extend() 135 | 136 | // Ensure existing views render with new theme 137 | // https://developer.apple.com/documentation/uikit/uiappearance 138 | application.windows.reload() 139 | } 140 | 141 | func extend() { 142 | // Optionally extend theme 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /ThemeKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8B1AA656215EAE2E007657FF /* With.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA655215EAE2E007657FF /* With.swift */; }; 11 | 8B1AA658215EAED6007657FF /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA657215EAED6007657FF /* UIView.swift */; }; 12 | 8B1AA65A215EAF04007657FF /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA659215EAF04007657FF /* UITableViewCell.swift */; }; 13 | 8B1AA65C215EAF43007657FF /* AppButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA65B215EAF43007657FF /* AppButton.swift */; }; 14 | 8B1AA660215EAF5D007657FF /* AppLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA65F215EAF5D007657FF /* AppLabel.swift */; }; 15 | 8B1AA662215EAF73007657FF /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA661215EAF73007657FF /* AppView.swift */; }; 16 | 8B1AA664215EAFBF007657FF /* UIWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA663215EAFBF007657FF /* UIWindow.swift */; }; 17 | 8B1AA666215EC593007657FF /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA665215EC593007657FF /* MainViewController.swift */; }; 18 | 8B1AA668215ED777007657FF /* AppSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA667215ED777007657FF /* AppSwitch.swift */; }; 19 | 8B1AA66A215EDD8E007657FF /* AppSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA669215EDD8E007657FF /* AppSegmentedControl.swift */; }; 20 | 8B1AA66C215FD109007657FF /* AppStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA66B215FD109007657FF /* AppStepper.swift */; }; 21 | 8B1AA66E215FD184007657FF /* AppSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B1AA66D215FD184007657FF /* AppSlider.swift */; }; 22 | 8B2086AE215D3AB0005EDF26 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B2086AD215D3AB0005EDF26 /* AppDelegate.swift */; }; 23 | 8B2086B5215D3AB0005EDF26 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8B2086B3215D3AB0005EDF26 /* Main.storyboard */; }; 24 | 8B2086B7215D3AB1005EDF26 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8B2086B6215D3AB1005EDF26 /* Assets.xcassets */; }; 25 | 8B2086BA215D3AB1005EDF26 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8B2086B8215D3AB1005EDF26 /* LaunchScreen.storyboard */; }; 26 | 8B2086C5215D3E68005EDF26 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B2086C4215D3E68005EDF26 /* Theme.swift */; }; 27 | 8B2086C8215D3E77005EDF26 /* LightTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B2086C7215D3E77005EDF26 /* LightTheme.swift */; }; 28 | 8B2086CA215D3E7E005EDF26 /* DarkTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B2086C9215D3E7E005EDF26 /* DarkTheme.swift */; }; 29 | 8B2086CC215D3E89005EDF26 /* OceanTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B2086CB215D3E89005EDF26 /* OceanTheme.swift */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 8B1AA655215EAE2E007657FF /* With.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = With.swift; sourceTree = ""; }; 34 | 8B1AA657215EAED6007657FF /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; 35 | 8B1AA659215EAF04007657FF /* UITableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; }; 36 | 8B1AA65B215EAF43007657FF /* AppButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppButton.swift; sourceTree = ""; }; 37 | 8B1AA65F215EAF5D007657FF /* AppLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLabel.swift; sourceTree = ""; }; 38 | 8B1AA661215EAF73007657FF /* AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = ""; }; 39 | 8B1AA663215EAFBF007657FF /* UIWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIWindow.swift; sourceTree = ""; }; 40 | 8B1AA665215EC593007657FF /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 41 | 8B1AA667215ED777007657FF /* AppSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSwitch.swift; sourceTree = ""; }; 42 | 8B1AA669215EDD8E007657FF /* AppSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSegmentedControl.swift; sourceTree = ""; }; 43 | 8B1AA66B215FD109007657FF /* AppStepper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStepper.swift; sourceTree = ""; }; 44 | 8B1AA66D215FD184007657FF /* AppSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSlider.swift; sourceTree = ""; }; 45 | 8B2086AA215D3AB0005EDF26 /* ThemeKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ThemeKit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 8B2086AD215D3AB0005EDF26 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 47 | 8B2086B4215D3AB0005EDF26 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | 8B2086B6215D3AB1005EDF26 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | 8B2086B9215D3AB1005EDF26 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | 8B2086BB215D3AB1005EDF26 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 8B2086C4215D3E68005EDF26 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; 52 | 8B2086C7215D3E77005EDF26 /* LightTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightTheme.swift; sourceTree = ""; }; 53 | 8B2086C9215D3E7E005EDF26 /* DarkTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarkTheme.swift; sourceTree = ""; }; 54 | 8B2086CB215D3E89005EDF26 /* OceanTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OceanTheme.swift; sourceTree = ""; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | 8B2086A7215D3AB0005EDF26 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | 8B2086A1215D3AB0005EDF26 = { 69 | isa = PBXGroup; 70 | children = ( 71 | 8B2086AC215D3AB0005EDF26 /* ThemeKit */, 72 | 8B2086AB215D3AB0005EDF26 /* Products */, 73 | ); 74 | sourceTree = ""; 75 | }; 76 | 8B2086AB215D3AB0005EDF26 /* Products */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 8B2086AA215D3AB0005EDF26 /* ThemeKit.app */, 80 | ); 81 | name = Products; 82 | sourceTree = ""; 83 | }; 84 | 8B2086AC215D3AB0005EDF26 /* ThemeKit */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 8B2086CE215D4120005EDF26 /* Extensions */, 88 | 8B2086C2215D3AF4005EDF26 /* Controls */, 89 | 8B2086C3215D3E59005EDF26 /* Styles */, 90 | 8B1AA665215EC593007657FF /* MainViewController.swift */, 91 | 8B2086AD215D3AB0005EDF26 /* AppDelegate.swift */, 92 | 8B2086B3215D3AB0005EDF26 /* Main.storyboard */, 93 | 8B2086B8215D3AB1005EDF26 /* LaunchScreen.storyboard */, 94 | 8B2086B6215D3AB1005EDF26 /* Assets.xcassets */, 95 | 8B2086BB215D3AB1005EDF26 /* Info.plist */, 96 | ); 97 | path = ThemeKit; 98 | sourceTree = ""; 99 | }; 100 | 8B2086C2215D3AF4005EDF26 /* Controls */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 8B1AA65B215EAF43007657FF /* AppButton.swift */, 104 | 8B1AA65F215EAF5D007657FF /* AppLabel.swift */, 105 | 8B1AA669215EDD8E007657FF /* AppSegmentedControl.swift */, 106 | 8B1AA66D215FD184007657FF /* AppSlider.swift */, 107 | 8B1AA66B215FD109007657FF /* AppStepper.swift */, 108 | 8B1AA667215ED777007657FF /* AppSwitch.swift */, 109 | 8B1AA661215EAF73007657FF /* AppView.swift */, 110 | ); 111 | path = Controls; 112 | sourceTree = ""; 113 | }; 114 | 8B2086C3215D3E59005EDF26 /* Styles */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 8B2086C6215D3E6C005EDF26 /* Themes */, 118 | 8B2086C4215D3E68005EDF26 /* Theme.swift */, 119 | ); 120 | path = Styles; 121 | sourceTree = ""; 122 | }; 123 | 8B2086C6215D3E6C005EDF26 /* Themes */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 8B2086C9215D3E7E005EDF26 /* DarkTheme.swift */, 127 | 8B2086C7215D3E77005EDF26 /* LightTheme.swift */, 128 | 8B2086CB215D3E89005EDF26 /* OceanTheme.swift */, 129 | ); 130 | path = Themes; 131 | sourceTree = ""; 132 | }; 133 | 8B2086CE215D4120005EDF26 /* Extensions */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 8B1AA659215EAF04007657FF /* UITableViewCell.swift */, 137 | 8B1AA657215EAED6007657FF /* UIView.swift */, 138 | 8B1AA663215EAFBF007657FF /* UIWindow.swift */, 139 | 8B1AA655215EAE2E007657FF /* With.swift */, 140 | ); 141 | path = Extensions; 142 | sourceTree = ""; 143 | }; 144 | /* End PBXGroup section */ 145 | 146 | /* Begin PBXNativeTarget section */ 147 | 8B2086A9215D3AB0005EDF26 /* ThemeKit */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = 8B2086BE215D3AB1005EDF26 /* Build configuration list for PBXNativeTarget "ThemeKit" */; 150 | buildPhases = ( 151 | 8B2086A6215D3AB0005EDF26 /* Sources */, 152 | 8B2086A7215D3AB0005EDF26 /* Frameworks */, 153 | 8B2086A8215D3AB0005EDF26 /* Resources */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | ); 159 | name = ThemeKit; 160 | productName = ThemeKit; 161 | productReference = 8B2086AA215D3AB0005EDF26 /* ThemeKit.app */; 162 | productType = "com.apple.product-type.application"; 163 | }; 164 | /* End PBXNativeTarget section */ 165 | 166 | /* Begin PBXProject section */ 167 | 8B2086A2215D3AB0005EDF26 /* Project object */ = { 168 | isa = PBXProject; 169 | attributes = { 170 | LastSwiftUpdateCheck = 1000; 171 | LastUpgradeCheck = 1000; 172 | ORGANIZATIONNAME = "Basem Emara"; 173 | TargetAttributes = { 174 | 8B2086A9215D3AB0005EDF26 = { 175 | CreatedOnToolsVersion = 10.0; 176 | }; 177 | }; 178 | }; 179 | buildConfigurationList = 8B2086A5215D3AB0005EDF26 /* Build configuration list for PBXProject "ThemeKit" */; 180 | compatibilityVersion = "Xcode 9.3"; 181 | developmentRegion = en; 182 | hasScannedForEncodings = 0; 183 | knownRegions = ( 184 | en, 185 | Base, 186 | ); 187 | mainGroup = 8B2086A1215D3AB0005EDF26; 188 | productRefGroup = 8B2086AB215D3AB0005EDF26 /* Products */; 189 | projectDirPath = ""; 190 | projectRoot = ""; 191 | targets = ( 192 | 8B2086A9215D3AB0005EDF26 /* ThemeKit */, 193 | ); 194 | }; 195 | /* End PBXProject section */ 196 | 197 | /* Begin PBXResourcesBuildPhase section */ 198 | 8B2086A8215D3AB0005EDF26 /* Resources */ = { 199 | isa = PBXResourcesBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | 8B2086BA215D3AB1005EDF26 /* LaunchScreen.storyboard in Resources */, 203 | 8B2086B7215D3AB1005EDF26 /* Assets.xcassets in Resources */, 204 | 8B2086B5215D3AB0005EDF26 /* Main.storyboard in Resources */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXResourcesBuildPhase section */ 209 | 210 | /* Begin PBXSourcesBuildPhase section */ 211 | 8B2086A6215D3AB0005EDF26 /* Sources */ = { 212 | isa = PBXSourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 8B1AA662215EAF73007657FF /* AppView.swift in Sources */, 216 | 8B1AA65A215EAF04007657FF /* UITableViewCell.swift in Sources */, 217 | 8B1AA658215EAED6007657FF /* UIView.swift in Sources */, 218 | 8B2086CA215D3E7E005EDF26 /* DarkTheme.swift in Sources */, 219 | 8B1AA664215EAFBF007657FF /* UIWindow.swift in Sources */, 220 | 8B2086C5215D3E68005EDF26 /* Theme.swift in Sources */, 221 | 8B1AA666215EC593007657FF /* MainViewController.swift in Sources */, 222 | 8B1AA656215EAE2E007657FF /* With.swift in Sources */, 223 | 8B1AA66A215EDD8E007657FF /* AppSegmentedControl.swift in Sources */, 224 | 8B2086CC215D3E89005EDF26 /* OceanTheme.swift in Sources */, 225 | 8B2086AE215D3AB0005EDF26 /* AppDelegate.swift in Sources */, 226 | 8B2086C8215D3E77005EDF26 /* LightTheme.swift in Sources */, 227 | 8B1AA660215EAF5D007657FF /* AppLabel.swift in Sources */, 228 | 8B1AA668215ED777007657FF /* AppSwitch.swift in Sources */, 229 | 8B1AA66E215FD184007657FF /* AppSlider.swift in Sources */, 230 | 8B1AA65C215EAF43007657FF /* AppButton.swift in Sources */, 231 | 8B1AA66C215FD109007657FF /* AppStepper.swift in Sources */, 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | /* End PBXSourcesBuildPhase section */ 236 | 237 | /* Begin PBXVariantGroup section */ 238 | 8B2086B3215D3AB0005EDF26 /* Main.storyboard */ = { 239 | isa = PBXVariantGroup; 240 | children = ( 241 | 8B2086B4215D3AB0005EDF26 /* Base */, 242 | ); 243 | name = Main.storyboard; 244 | sourceTree = ""; 245 | }; 246 | 8B2086B8215D3AB1005EDF26 /* LaunchScreen.storyboard */ = { 247 | isa = PBXVariantGroup; 248 | children = ( 249 | 8B2086B9215D3AB1005EDF26 /* Base */, 250 | ); 251 | name = LaunchScreen.storyboard; 252 | sourceTree = ""; 253 | }; 254 | /* End PBXVariantGroup section */ 255 | 256 | /* Begin XCBuildConfiguration section */ 257 | 8B2086BC215D3AB1005EDF26 /* Debug */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | CLANG_ANALYZER_NONNULL = YES; 262 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 264 | CLANG_CXX_LIBRARY = "libc++"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_ENABLE_OBJC_WEAK = YES; 268 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 269 | CLANG_WARN_BOOL_CONVERSION = YES; 270 | CLANG_WARN_COMMA = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INFINITE_RECURSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 281 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 284 | CLANG_WARN_STRICT_PROTOTYPES = YES; 285 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 286 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | CODE_SIGN_IDENTITY = "iPhone Developer"; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = dwarf; 292 | ENABLE_STRICT_OBJC_MSGSEND = YES; 293 | ENABLE_TESTABILITY = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu11; 295 | GCC_DYNAMIC_NO_PIC = NO; 296 | GCC_NO_COMMON_BLOCKS = YES; 297 | GCC_OPTIMIZATION_LEVEL = 0; 298 | GCC_PREPROCESSOR_DEFINITIONS = ( 299 | "DEBUG=1", 300 | "$(inherited)", 301 | ); 302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 303 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 304 | GCC_WARN_UNDECLARED_SELECTOR = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 306 | GCC_WARN_UNUSED_FUNCTION = YES; 307 | GCC_WARN_UNUSED_VARIABLE = YES; 308 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 309 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 310 | MTL_FAST_MATH = YES; 311 | ONLY_ACTIVE_ARCH = YES; 312 | SDKROOT = iphoneos; 313 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 314 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 315 | }; 316 | name = Debug; 317 | }; 318 | 8B2086BD215D3AB1005EDF26 /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ALWAYS_SEARCH_USER_PATHS = NO; 322 | CLANG_ANALYZER_NONNULL = YES; 323 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_ENABLE_OBJC_WEAK = YES; 329 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 330 | CLANG_WARN_BOOL_CONVERSION = YES; 331 | CLANG_WARN_COMMA = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 334 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 335 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 336 | CLANG_WARN_EMPTY_BODY = YES; 337 | CLANG_WARN_ENUM_CONVERSION = YES; 338 | CLANG_WARN_INFINITE_RECURSION = YES; 339 | CLANG_WARN_INT_CONVERSION = YES; 340 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 341 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 342 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 343 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 344 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 345 | CLANG_WARN_STRICT_PROTOTYPES = YES; 346 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 347 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 348 | CLANG_WARN_UNREACHABLE_CODE = YES; 349 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 350 | CODE_SIGN_IDENTITY = "iPhone Developer"; 351 | COPY_PHASE_STRIP = NO; 352 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 353 | ENABLE_NS_ASSERTIONS = NO; 354 | ENABLE_STRICT_OBJC_MSGSEND = YES; 355 | GCC_C_LANGUAGE_STANDARD = gnu11; 356 | GCC_NO_COMMON_BLOCKS = YES; 357 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 358 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 359 | GCC_WARN_UNDECLARED_SELECTOR = YES; 360 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 361 | GCC_WARN_UNUSED_FUNCTION = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 364 | MTL_ENABLE_DEBUG_INFO = NO; 365 | MTL_FAST_MATH = YES; 366 | SDKROOT = iphoneos; 367 | SWIFT_COMPILATION_MODE = wholemodule; 368 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 369 | VALIDATE_PRODUCT = YES; 370 | }; 371 | name = Release; 372 | }; 373 | 8B2086BF215D3AB1005EDF26 /* Debug */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 377 | CODE_SIGN_IDENTITY = "iPhone Developer"; 378 | CODE_SIGN_STYLE = Manual; 379 | DEVELOPMENT_TEAM = ""; 380 | INFOPLIST_FILE = ThemeKit/Info.plist; 381 | LD_RUNPATH_SEARCH_PATHS = ( 382 | "$(inherited)", 383 | "@executable_path/Frameworks", 384 | ); 385 | PRODUCT_BUNDLE_IDENTIFIER = com.basememara.ThemeKit; 386 | PRODUCT_NAME = "$(TARGET_NAME)"; 387 | PROVISIONING_PROFILE_SPECIFIER = ""; 388 | SWIFT_VERSION = 4.2; 389 | TARGETED_DEVICE_FAMILY = "1,2"; 390 | }; 391 | name = Debug; 392 | }; 393 | 8B2086C0215D3AB1005EDF26 /* Release */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 397 | CODE_SIGN_IDENTITY = "iPhone Developer"; 398 | CODE_SIGN_STYLE = Manual; 399 | DEVELOPMENT_TEAM = ""; 400 | INFOPLIST_FILE = ThemeKit/Info.plist; 401 | LD_RUNPATH_SEARCH_PATHS = ( 402 | "$(inherited)", 403 | "@executable_path/Frameworks", 404 | ); 405 | PRODUCT_BUNDLE_IDENTIFIER = com.basememara.ThemeKit; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | PROVISIONING_PROFILE_SPECIFIER = ""; 408 | SWIFT_VERSION = 4.2; 409 | TARGETED_DEVICE_FAMILY = "1,2"; 410 | }; 411 | name = Release; 412 | }; 413 | /* End XCBuildConfiguration section */ 414 | 415 | /* Begin XCConfigurationList section */ 416 | 8B2086A5215D3AB0005EDF26 /* Build configuration list for PBXProject "ThemeKit" */ = { 417 | isa = XCConfigurationList; 418 | buildConfigurations = ( 419 | 8B2086BC215D3AB1005EDF26 /* Debug */, 420 | 8B2086BD215D3AB1005EDF26 /* Release */, 421 | ); 422 | defaultConfigurationIsVisible = 0; 423 | defaultConfigurationName = Release; 424 | }; 425 | 8B2086BE215D3AB1005EDF26 /* Build configuration list for PBXNativeTarget "ThemeKit" */ = { 426 | isa = XCConfigurationList; 427 | buildConfigurations = ( 428 | 8B2086BF215D3AB1005EDF26 /* Debug */, 429 | 8B2086C0215D3AB1005EDF26 /* Release */, 430 | ); 431 | defaultConfigurationIsVisible = 0; 432 | defaultConfigurationName = Release; 433 | }; 434 | /* End XCConfigurationList section */ 435 | }; 436 | rootObject = 8B2086A2215D3AB0005EDF26 /* Project object */; 437 | } 438 | -------------------------------------------------------------------------------- /ThemeKit/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 | 43 | 49 | 55 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 77 | 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 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 152 | 158 | 164 | 170 | 171 | 172 | 173 | 174 | 175 | 179 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 254 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 278 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 302 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 330 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 354 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 378 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 406 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 430 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 454 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | --------------------------------------------------------------------------------