├── 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 |
--------------------------------------------------------------------------------