From 848d5a3a4a86ccf92effdcb9e4ab3e98b026ef60 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 4 Oct 2018 22:26:15 +0400 Subject: [PATCH 01/36] Added Theme --- Material.xcodeproj/project.pbxproj | 12 +++ Sources/iOS/Theme.swift | 122 +++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 Sources/iOS/Theme.swift diff --git a/Material.xcodeproj/project.pbxproj b/Material.xcodeproj/project.pbxproj index c3d62b1e6..612f9a34a 100644 --- a/Material.xcodeproj/project.pbxproj +++ b/Material.xcodeproj/project.pbxproj @@ -170,6 +170,7 @@ 96E3C39A1D3A1CC20086A024 /* ErrorTextField.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961F18E71CD93E3E008927C5 /* ErrorTextField.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96E3C39C1D3A1CC20086A024 /* Offset.swift in Headers */ = {isa = PBXBuildFile; fileRef = 968C99461D377849000074FF /* Offset.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96F1A5531F24F17A001D8CAF /* TabsController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E09DC71F2287E50000B121 /* TabsController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; + 9D00EBB4216675FB00DBCD69 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D00EBB3216675FB00DBCD69 /* Theme.swift */; }; 9D054A6520D175AC00D0528D /* Material+UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6320D175AC00D0528D /* Material+UIButton.swift */; }; 9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6420D175AC00D0528D /* Material+UILabel.swift */; }; 9D39A81B20FE8ED100BA8FA1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */; }; @@ -292,6 +293,7 @@ 96E09DC71F2287E50000B121 /* TabsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsController.swift; sourceTree = ""; }; 96E3C3931D397AE90086A024 /* Material+UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIView.swift"; sourceTree = ""; }; 96F1DC871D654FDF0025F925 /* Material+CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+CALayer.swift"; sourceTree = ""; }; + 9D00EBB3216675FB00DBCD69 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; 9D054A6320D175AC00D0528D /* Material+UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIButton.swift"; sourceTree = ""; }; 9D054A6420D175AC00D0528D /* Material+UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UILabel.swift"; sourceTree = ""; }; 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -567,6 +569,7 @@ 963FBF011D6696AB008F8512 /* Tab */, 966ECF2B1CF4C21B00BB0BDF /* Table */, 96090B031D9D709E00709CA6 /* Text */, + 9D00EBB2216675A800DBCD69 /* Theme */, 963FBF001D66964F008F8512 /* Toolbar */, 9626CA951DAB5370003E2611 /* Transition */, 96BCB8061CB40FD000C806FE /* Type */, @@ -760,6 +763,14 @@ name = Animation; sourceTree = ""; }; + 9D00EBB2216675A800DBCD69 /* Theme */ = { + isa = PBXGroup; + children = ( + 9D00EBB3216675FB00DBCD69 /* Theme.swift */, + ); + name = Theme; + sourceTree = ""; + }; 9DE84D6E1FF0250E00586C8B /* ButtonGroup */ = { isa = PBXGroup; children = ( @@ -1002,6 +1013,7 @@ 966C17731F0439F600D3E83C /* Material+MotionAnimation.swift in Sources */, 965E80E51DD4C53300D61E4B /* PulseAnimation.swift in Sources */, 9DE84D721FF0252600586C8B /* RadioButtonGroup.swift in Sources */, + 9D00EBB4216675FB00DBCD69 /* Theme.swift in Sources */, 965E80FE1DD4D59500D61E4B /* ToolbarController.swift in Sources */, 96328B971E05C0BB009A4C90 /* TableView.swift in Sources */, 965E80F81DD4D59500D61E4B /* ImageCard.swift in Sources */, diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift new file mode 100644 index 000000000..97726e981 --- /dev/null +++ b/Sources/iOS/Theme.swift @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 - 2018, Daniel Dahan and CosmicMind, Inc. . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of CosmicMind nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import UIKit +import Motion + +public protocol Themeable { + func apply(theme: Theme) +} + +public struct Theme { + /// The color displayed most frequently across the app. + public var primary = Color.blue.base + + /// Accent color for some components such as FABMenu. + public var secondary = Color.blue.base + + /// Background color for view controllers and some components. + public var background = Color.white + + /// Background color for components such as cards, and dialogs. + public var surface = Color.white + + /// Error color for components such as ErrorTextField. + public var error = Color.red.base + + + /// Text and iconography color to be used on primary color. + public var onPrimary = Color.white + + /// Text and iconography color to be used on secondary color. + public var onSecondary = Color.white + + /// Text and iconography color to be used on background color. + public var onBackground = Color.black + + /// Text and iconography color to be used on surface color. + public var onSurface = Color.black + + /// Text and iconography color to be used on error color. + public var onError = Color.white +} + +public extension Theme { + /// Current theme for Material. + static var current = Theme.light { + didSet { + guard let v = Application.rootViewController else { + return + } + + apply(theme: .current, to: v) + } + } + + /// A light theme. + static var light = Theme() + + /// A dark theme. + static var dark: Theme = { + var t = Theme() + t.primary = UIColor(rgb: 0x202020) + t.secondary = UIColor(rgb: 0x33776B) + t.background = UIColor(rgb: 0x303030) + t.onBackground = .white + return t + }() + + /** + Applies theme to the hierarchy of given view. + - Parameter theme: A Theme. + - Parameter to view: A UIView. + */ + static func apply(theme: Theme, to view: UIView) { + (view as? Themeable)?.apply(theme: theme) + + view.subviews.forEach { + apply(theme: theme, to: $0) + } + } + + /** + Applies theme to the hierarchy of given view controller. + - Parameter theme: A Theme. + - Parameter to viewController: A UIViewController. + */ + static func apply(theme: Theme, to viewController: UIViewController) { + apply(theme: theme, to: viewController.view) + (viewController as? Themeable)?.apply(theme: theme) + + viewController.children.forEach { + apply(theme: theme, to: $0) + } + } +} From 16d1f535c09a66746caa5029cd4fa5d43ce21c25 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 4 Oct 2018 23:50:54 +0400 Subject: [PATCH 02/36] Disallow setting Theme.current directly --- Sources/iOS/Theme.swift | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index 97726e981..672361c4e 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -70,15 +70,7 @@ public struct Theme { public extension Theme { /// Current theme for Material. - static var current = Theme.light { - didSet { - guard let v = Application.rootViewController else { - return - } - - apply(theme: .current, to: v) - } - } + static private(set) var current = Theme.light /// A light theme. static var light = Theme() @@ -92,6 +84,21 @@ public extension Theme { t.onBackground = .white return t }() +} + +public extension Theme { + /** + Applies theme to the entire app. + - Parameter theme: A Theme. + */ + static func apply(theme: Theme) { + guard let v = Application.rootViewController else { + return + } + + current = theme + apply(theme: theme, to: v) + } /** Applies theme to the hierarchy of given view. From ca4a485d7b8e9e9f02d0f3bcc3bdbe1b309ef989 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 4 Oct 2018 23:51:23 +0400 Subject: [PATCH 03/36] Added empty Theme initializer --- Sources/iOS/Theme.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index 672361c4e..3a0132e43 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -66,6 +66,9 @@ public struct Theme { /// Text and iconography color to be used on error color. public var onError = Color.white + + /// An initializer. + public init() { } } public extension Theme { From e9fc6bb681c0cc4a103279d0f8045c9687e5d595 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Fri, 5 Oct 2018 00:47:13 +0400 Subject: [PATCH 04/36] Added applying theme for only components created in a code block --- Sources/iOS/Theme.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index 3a0132e43..b9699065a 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -129,4 +129,17 @@ public extension Theme { apply(theme: theme, to: $0) } } + + /** + Applies provided theme for the components created within the given block + without chaging app's theme. + - Parameter theme: A Theme. + - Parameter for block: A code block. + */ + static func applying(theme: Theme, for execute: () -> Void) { + let v = current + current = theme + execute() + current = v + } } From 3c21dd3e8a3fd1f02924b8c8bf700509b0a723a9 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 00:21:52 +0400 Subject: [PATCH 05/36] Added isThemingEnabled --- Sources/iOS/Theme.swift | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index b9699065a..d480e7c33 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -32,7 +32,14 @@ import UIKit import Motion public protocol Themeable { + /** + Applies given theme. + - Parameter theme: A Theme. + */ func apply(theme: Theme) + + /// A boolean indicating if theming is enabled. + var isThemingEnabled: Bool { get set } } public struct Theme { @@ -109,6 +116,10 @@ public extension Theme { - Parameter to view: A UIView. */ static func apply(theme: Theme, to view: UIView) { + guard !((view as? Themeable)?.isThemingEnabled == false) else { + return + } + (view as? Themeable)?.apply(theme: theme) view.subviews.forEach { @@ -122,6 +133,10 @@ public extension Theme { - Parameter to viewController: A UIViewController. */ static func apply(theme: Theme, to viewController: UIViewController) { + guard !((viewController as? Themeable)?.isThemingEnabled == false) else { + return + } + apply(theme: theme, to: viewController.view) (viewController as? Themeable)?.apply(theme: theme) @@ -143,3 +158,21 @@ public extension Theme { current = v } } + + +/// A memory reference to the isThemingEnabled for Themeable NSObject subclasses. +fileprivate var IsThemingEnabledKey: UInt8 = 0 + +public extension Themeable where Self: NSObject { + /// A boolean indicating if theming is enabled. + var isThemingEnabled: Bool { + get { + return AssociatedObject.get(base: self, key: &IsThemingEnabledKey) { + true + } + } + set(value) { + AssociatedObject.set(base: self, key: &IsThemingEnabledKey, value: value) + } + } +} From 97787b5f6e3aad1ec38ffcefc8ec4be4b78a3c6a Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 01:25:32 +0400 Subject: [PATCH 06/36] Fixed cannot assign to let constant issue for isThemingEnabled --- Sources/iOS/Theme.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index d480e7c33..20312e9d4 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -31,7 +31,7 @@ import UIKit import Motion -public protocol Themeable { +public protocol Themeable: class { /** Applies given theme. - Parameter theme: A Theme. From 6eff97cd322d034211d80c1a6b6c8d712a5adf44 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 01:27:44 +0400 Subject: [PATCH 07/36] Changed theme application order to `subviews first` --- Sources/iOS/Theme.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index 20312e9d4..103062e17 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -120,11 +120,11 @@ public extension Theme { return } - (view as? Themeable)?.apply(theme: theme) - view.subviews.forEach { apply(theme: theme, to: $0) } + + (view as? Themeable)?.apply(theme: theme) } /** @@ -137,12 +137,12 @@ public extension Theme { return } - apply(theme: theme, to: viewController.view) - (viewController as? Themeable)?.apply(theme: theme) - viewController.children.forEach { apply(theme: theme, to: $0) } + + apply(theme: theme, to: viewController.view) + (viewController as? Themeable)?.apply(theme: theme) } /** @@ -161,7 +161,7 @@ public extension Theme { /// A memory reference to the isThemingEnabled for Themeable NSObject subclasses. -fileprivate var IsThemingEnabledKey: UInt8 = 0 +private var IsThemingEnabledKey: UInt8 = 0 public extension Themeable where Self: NSObject { /// A boolean indicating if theming is enabled. From a14404fe61465d147d5223eb5696253746c1a943 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 01:28:33 +0400 Subject: [PATCH 08/36] Fixed Theme.current not set when there is no rootViewController --- Sources/iOS/Theme.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index 103062e17..7de300805 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -102,11 +102,11 @@ public extension Theme { - Parameter theme: A Theme. */ static func apply(theme: Theme) { + current = theme guard let v = Application.rootViewController else { return } - current = theme apply(theme: theme, to: v) } From c217102cda6a806ece09d97d94b098b4bc1eab19 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 16:26:58 +0400 Subject: [PATCH 09/36] Added applyCurrentTheme for Themeables --- Sources/iOS/Theme.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index 7de300805..ac0d146a2 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -89,9 +89,10 @@ public extension Theme { static var dark: Theme = { var t = Theme() t.primary = UIColor(rgb: 0x202020) - t.secondary = UIColor(rgb: 0x33776B) + t.secondary = Color.teal.base t.background = UIColor(rgb: 0x303030) t.onBackground = .white + t.onSurface = .white return t }() } @@ -175,4 +176,13 @@ public extension Themeable where Self: NSObject { AssociatedObject.set(base: self, key: &IsThemingEnabledKey, value: value) } } + + /// Applies current theme to itself if theming is enabled. + internal func applyCurrentTheme() { + guard isThemingEnabled else { + return + } + + apply(theme: .current) + } } From b5908d8339ab6327abb5bc340c3ef9e182dfb289 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 16:33:29 +0400 Subject: [PATCH 10/36] Made buttons themeable --- Sources/iOS/BaseIconLayerButton.swift | 8 ++++++++ Sources/iOS/Button.swift | 9 ++++++++- Sources/iOS/CheckButton.swift | 7 +++++++ Sources/iOS/FABButton.swift | 10 +++++++++- Sources/iOS/FlatButton.swift | 9 +++++++++ Sources/iOS/IconButton.swift | 7 +++++++ Sources/iOS/RaisedButton.swift | 10 +++++++++- 7 files changed, 57 insertions(+), 3 deletions(-) diff --git a/Sources/iOS/BaseIconLayerButton.swift b/Sources/iOS/BaseIconLayerButton.swift index 83c2a1b00..dfe094fda 100644 --- a/Sources/iOS/BaseIconLayerButton.swift +++ b/Sources/iOS/BaseIconLayerButton.swift @@ -142,6 +142,14 @@ open class BaseIconLayerButton: Button { reloadImage() } } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + setIconColor(theme.secondary, for: .selected) + setIconColor(theme.onSurface.withAlphaComponent(0.38), for: .normal) + titleColor = theme.onSurface.withAlphaComponent(0.60) + } /// This might be considered as a hackish way, but it's just manipulation diff --git a/Sources/iOS/Button.swift b/Sources/iOS/Button.swift index ceb378531..296745354 100644 --- a/Sources/iOS/Button.swift +++ b/Sources/iOS/Button.swift @@ -31,7 +31,7 @@ import UIKit import Motion -open class Button: UIButton, Pulseable, PulseableLayer { +open class Button: UIButton, Pulseable, PulseableLayer, Themeable { /** A CAShapeLayer used to manage elements that would be affected by the clipToBounds property of the backing layer. For example, this @@ -281,7 +281,14 @@ open class Button: UIButton, Pulseable, PulseableLayer { contentScaleFactor = Screen.scale prepareVisualLayer() preparePulse() + applyCurrentTheme() } + + /** + Applies given theme to the view. + - Parameter theme: A Theme. + */ + open func apply(theme: Theme) { } } extension Button { diff --git a/Sources/iOS/CheckButton.swift b/Sources/iOS/CheckButton.swift index 3804b9e80..3033a8d80 100644 --- a/Sources/iOS/CheckButton.swift +++ b/Sources/iOS/CheckButton.swift @@ -31,6 +31,12 @@ open class CheckButton: BaseIconLayerButton { guard !isAnimating else { return } setSelected(!isSelected, animated: true) } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + checkmarkColor = theme.onSecondary + } } internal class CheckBoxLayer: BaseIconLayer { @@ -86,6 +92,7 @@ internal class CheckBoxLayer: BaseIconLayer { if isSelected { borderLayer.borderWidth = borderLayerNormalBorderWidth } else { + borderLayer.borderWidth = 0 borderLayer.backgroundColor = (isEnabled ? normalColor : disabledColor).cgColor checkMarkLeftLayer.strokeEnd = 1 checkMarkRightLayer.strokeEnd = 1 diff --git a/Sources/iOS/FABButton.swift b/Sources/iOS/FABButton.swift index 2650f54f5..b3c0b5cf4 100644 --- a/Sources/iOS/FABButton.swift +++ b/Sources/iOS/FABButton.swift @@ -36,6 +36,14 @@ open class FABButton: Button { depthPreset = .depth1 shapePreset = .circle pulseAnimation = .centerWithBacking - backgroundColor = .white + } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + backgroundColor = theme.secondary + titleColor = theme.onSecondary + tintColor = theme.onSecondary + pulseColor = theme.onSecondary } } diff --git a/Sources/iOS/FlatButton.swift b/Sources/iOS/FlatButton.swift index 705a19399..daf2891b6 100644 --- a/Sources/iOS/FlatButton.swift +++ b/Sources/iOS/FlatButton.swift @@ -35,4 +35,13 @@ open class FlatButton: Button { super.prepare() cornerRadiusPreset = .cornerRadius1 } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + backgroundColor = .clear + titleColor = theme.secondary + tintColor = theme.secondary + pulseColor = theme.secondary + } } diff --git a/Sources/iOS/IconButton.swift b/Sources/iOS/IconButton.swift index d4559f68f..92c6bf0d5 100644 --- a/Sources/iOS/IconButton.swift +++ b/Sources/iOS/IconButton.swift @@ -35,4 +35,11 @@ open class IconButton: Button { super.prepare() pulseAnimation = .center } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + tintColor = theme.secondary + pulseColor = theme.secondary + } } diff --git a/Sources/iOS/RaisedButton.swift b/Sources/iOS/RaisedButton.swift index d588a60aa..ed4200089 100644 --- a/Sources/iOS/RaisedButton.swift +++ b/Sources/iOS/RaisedButton.swift @@ -35,6 +35,14 @@ open class RaisedButton: Button { super.prepare() depthPreset = .depth1 cornerRadiusPreset = .cornerRadius1 - backgroundColor = .white + } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + backgroundColor = theme.secondary + titleColor = theme.onSecondary + pulseColor = theme.onSecondary + tintColor = theme.onSecondary } } From c08a143e9500b21ca0e527279f7bb5e99566e27a Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 16:35:36 +0400 Subject: [PATCH 11/36] Made TextFields themeable --- Sources/iOS/ErrorTextField.swift | 6 ++++++ Sources/iOS/TextField.swift | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Sources/iOS/ErrorTextField.swift b/Sources/iOS/ErrorTextField.swift index 9d1e749bd..6ae6f208d 100644 --- a/Sources/iOS/ErrorTextField.swift +++ b/Sources/iOS/ErrorTextField.swift @@ -93,4 +93,10 @@ open class ErrorTextField: TextField { super.layoutSubviews() layoutBottomLabel(label: errorLabel, verticalOffset: errorVerticalOffset) } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + errorColor = theme.error + } } diff --git a/Sources/iOS/TextField.swift b/Sources/iOS/TextField.swift index 63791edc7..b5dbf1433 100644 --- a/Sources/iOS/TextField.swift +++ b/Sources/iOS/TextField.swift @@ -63,7 +63,7 @@ public protocol TextFieldDelegate: UITextFieldDelegate { optional func textField(textField: TextField, didClear text: String?) } -open class TextField: UITextField { +open class TextField: UITextField, Themeable { /// Minimum TextField text height. private let minimumTextHeight: CGFloat = 32 @@ -459,6 +459,21 @@ open class TextField: UITextField { prepareTargetHandlers() prepareTextAlignment() prepareRightView() + applyCurrentTheme() + } + + open func apply(theme: Theme) { + placeholderActiveColor = theme.secondary + placeholderNormalColor = theme.onSurface.withAlphaComponent(0.38) + + leftViewActiveColor = theme.secondary + leftViewNormalColor = theme.onSurface.withAlphaComponent(0.38) + + dividerActiveColor = theme.secondary + dividerNormalColor = theme.onSurface.withAlphaComponent(0.12) + + detailColor = theme.onSurface.withAlphaComponent(0.38) + textColor = theme.onSurface.withAlphaComponent(0.87) } } From 85d97b1737cbf836cadc257e19db6ddeeb5eec68 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 16:37:41 +0400 Subject: [PATCH 12/36] Made NavigationBar themeable --- Sources/iOS/NavigationBar.swift | 34 ++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/Sources/iOS/NavigationBar.swift b/Sources/iOS/NavigationBar.swift index b457f6eff..7da656299 100644 --- a/Sources/iOS/NavigationBar.swift +++ b/Sources/iOS/NavigationBar.swift @@ -30,7 +30,7 @@ import UIKit -open class NavigationBar: UINavigationBar { +open class NavigationBar: UINavigationBar, Themeable { /// Will layout the view. open var willLayout: Bool { return 0 < bounds.width && 0 < bounds.height && nil != superview @@ -168,7 +168,32 @@ open class NavigationBar: UINavigationBar { let image = UIImage() shadowImage = image setBackgroundImage(image, for: .default) - backgroundColor = .white + applyCurrentTheme() + } + + open func apply(theme: Theme) { + backgroundColor = theme.primary + items?.forEach { + apply(theme: theme, to: $0) + } + } + + private func apply(theme: Theme, to item: UINavigationItem) { + item.toolbar.backgroundColor = .clear + (item.toolbar.leftViews + item.toolbar.rightViews + item.toolbar.centerViews).compactMap { $0 as? IconButton } + .filter { $0.isThemingEnabled } + .forEach { + $0.tintColor = theme.onPrimary + $0.pulseColor = theme.onPrimary + } + + if !((item.titleLabel as? Themeable)?.isThemingEnabled == false) { + item.titleLabel.textColor = theme.onPrimary + } + + if !((item.detailLabel as? Themeable)?.isThemingEnabled == false) { + item.detailLabel.textColor = theme.onPrimary + } } } @@ -182,8 +207,11 @@ internal extension NavigationBar { return } + if isThemingEnabled { + apply(theme: .current, to: item) + } + let toolbar = item.toolbar - toolbar.backgroundColor = .clear toolbar.interimSpace = interimSpace toolbar.contentEdgeInsets = contentEdgeInsets From 8ec40dd595cc1608bdd8ee2da5bd6b6372a780ab Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 16:41:35 +0400 Subject: [PATCH 13/36] Made ViewController themeable --- Sources/iOS/ViewController.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Sources/iOS/ViewController.swift b/Sources/iOS/ViewController.swift index ea515595b..e8acdf6f3 100644 --- a/Sources/iOS/ViewController.swift +++ b/Sources/iOS/ViewController.swift @@ -30,7 +30,7 @@ import UIKit -open class ViewController: UIViewController { +open class ViewController: UIViewController, Themeable { open override func viewDidLoad() { super.viewDidLoad() prepare() @@ -45,8 +45,8 @@ open class ViewController: UIViewController { */ open func prepare() { view.clipsToBounds = true - view.backgroundColor = .white view.contentScaleFactor = Screen.scale + applyCurrentTheme() } open override func viewWillLayoutSubviews() { @@ -61,4 +61,12 @@ open class ViewController: UIViewController { have a certain need. */ open func layoutSubviews() { } + + /** + Applies given theme to the view controller. + - Parameter theme: A Theme. + */ + open func apply(theme: Theme) { + view.backgroundColor = theme.background + } } From 93e8f1a1439a3b4ea837603a2b23b10395660261 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 16:48:54 +0400 Subject: [PATCH 14/36] Fixed debug view hierarchy not working when we have Check/RadioButton --- Sources/iOS/BaseIconLayerButton.swift | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Sources/iOS/BaseIconLayerButton.swift b/Sources/iOS/BaseIconLayerButton.swift index dfe094fda..86bff4b70 100644 --- a/Sources/iOS/BaseIconLayerButton.swift +++ b/Sources/iOS/BaseIconLayerButton.swift @@ -84,6 +84,7 @@ open class BaseIconLayerButton: Button { open override func prepare() { super.prepare() layer.addSublayer(iconLayer) + iconLayer.prepare() contentHorizontalAlignment = .left // default was .center reloadImage() } @@ -193,16 +194,6 @@ internal class BaseIconLayer: CALayer { } } - override init() { - super.init() - prepare() - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - prepare() - } - func prepare() { normalColor = { normalColor }() // calling didSet selectedColor = { selectedColor }() // calling didSet From 6a22d0ec015ccccc9d6542a4a9de52115e62f9e1 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 16:50:14 +0400 Subject: [PATCH 15/36] Adjusted CheckButton and RadioButton icon --- Sources/iOS/BaseIconLayerButton.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/iOS/BaseIconLayerButton.swift b/Sources/iOS/BaseIconLayerButton.swift index 86bff4b70..99e183d88 100644 --- a/Sources/iOS/BaseIconLayerButton.swift +++ b/Sources/iOS/BaseIconLayerButton.swift @@ -123,7 +123,7 @@ open class BaseIconLayerButton: Button { /// /// This property affects `intrinsicContentSize` and `sizeThatFits(_:)` /// Use `iconEdgeInsets` to set margins. - open var iconSize: CGFloat = 16 { + open var iconSize: CGFloat = 18 { didSet { reloadImage() } @@ -137,8 +137,8 @@ open class BaseIconLayerButton: Button { /// /// You can use `iconSize` and this property, or `titleEdgeInsets` and `contentEdgeInsets` to position /// the icon however you want. - /// For negative values, behavior is undefined. Default is `5.0` for all four margins - open var iconEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5) { + /// For negative values, behavior is undefined. Default is `8.0` for all four margins + open var iconEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) { didSet { reloadImage() } From f7ba4c9e5edd5f26a7c7e5150f9e7d839de4faa0 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Sat, 6 Oct 2018 16:51:02 +0400 Subject: [PATCH 16/36] Added state-aware pulse coloring for Radio and CheckButton --- Sources/iOS/BaseIconLayerButton.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Sources/iOS/BaseIconLayerButton.swift b/Sources/iOS/BaseIconLayerButton.swift index 99e183d88..60a4e206e 100644 --- a/Sources/iOS/BaseIconLayerButton.swift +++ b/Sources/iOS/BaseIconLayerButton.swift @@ -20,6 +20,7 @@ open class BaseIconLayerButton: Button { open override var isSelected: Bool { didSet { iconLayer.setSelected(isSelected, animated: false) + updatePulseColor() } } @@ -150,6 +151,10 @@ open class BaseIconLayerButton: Button { setIconColor(theme.secondary, for: .selected) setIconColor(theme.onSurface.withAlphaComponent(0.38), for: .normal) titleColor = theme.onSurface.withAlphaComponent(0.60) + + selectedPulseColor = theme.secondary + normalPulseColor = theme.onSurface + updatePulseColor() } @@ -168,6 +173,18 @@ open class BaseIconLayerButton: Button { UIGraphicsEndImageContext() self.image = image } + + /// Pulse color for selected state. + open var selectedPulseColor = Color.white + + /// Pulse color for normal state. + open var normalPulseColor = Color.white +} + +private extension BaseIconLayerButton { + func updatePulseColor() { + pulseColor = isSelected ? selectedPulseColor : normalPulseColor + } } // MARK: - BaseIconLayer From 7cd9489f52cf54b1d1db2f2cf12a1a8300cb303a Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Tue, 9 Oct 2018 18:56:06 +0400 Subject: [PATCH 17/36] Added UIColor.blend(with:) --- Sources/iOS/Material+UIColor.swift | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Sources/iOS/Material+UIColor.swift b/Sources/iOS/Material+UIColor.swift index 66a5d81cf..21171fded 100644 --- a/Sources/iOS/Material+UIColor.swift +++ b/Sources/iOS/Material+UIColor.swift @@ -62,3 +62,40 @@ public extension UIColor { self.init(argb: (0xff000000 as UInt32) | rgb) } } + +internal extension UIColor { + /// A tuple of the rgba components. + var components: (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) { + var r: CGFloat = 0 + var g: CGFloat = 0 + var b: CGFloat = 0 + var a: CGFloat = 0 + + getRed(&r, green: &g, blue: &b, alpha: &a) + + return (r, g, b, a) + } + + /** + Blends given coverColor over this color. + - Parameter with coverColor: A UIColor. + - Returns: Resultant color of blending. + */ + func blend(with coverColor: UIColor) -> UIColor { + + /// Blends channels according to https://en.wikipedia.org/wiki/Alpha_compositing (see `over` operator). + func blendChannel(value: CGFloat, bValue: CGFloat, alpha: CGFloat, bAlpha: CGFloat) -> CGFloat { + return ((1 - alpha) * bValue * bAlpha + alpha * value) / (alpha + bAlpha * (1 - alpha)) + } + + let (r, g, b, a) = coverColor.components + let (bR, bG, bB, bA) = components + + let newR = blendChannel(value: r, bValue: bR, alpha: a, bAlpha: bA) + let newG = blendChannel(value: g, bValue: bG, alpha: a, bAlpha: bA) + let newB = blendChannel(value: b, bValue: bB, alpha: a, bAlpha: bA) + let newA = a + bA * (1 - a) + + return UIColor(red: newR, green: newG, blue: newB, alpha: newA) + } +} From 4728c88a80c29eed54fe0320251852026eefdade Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Tue, 9 Oct 2018 19:02:46 +0400 Subject: [PATCH 18/36] Made Switch themeable --- Sources/iOS/Switch.swift | 52 +++++++++++----------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/Sources/iOS/Switch.swift b/Sources/iOS/Switch.swift index e673c0ae3..bca255e7d 100644 --- a/Sources/iOS/Switch.swift +++ b/Sources/iOS/Switch.swift @@ -59,7 +59,7 @@ public protocol SwitchDelegate { func switchDidChangeState(control: Switch, state: SwitchState) } -open class Switch: UIControl { +open class Switch: UIControl, Themeable { /// Will layout the view. open var willLayout: Bool { return 0 < bounds.width && 0 < bounds.height && nil != superview @@ -200,32 +200,6 @@ open class Switch: UIControl { } } - /// Switch style. - open var switchStyle = SwitchStyle.dark { - didSet { - switch switchStyle { - case .light: - buttonOnColor = Color.blue.darken2 - trackOnColor = Color.blue.lighten3 - buttonOffColor = Color.blueGrey.lighten4 - trackOffColor = Color.grey.lighten2 - buttonOnDisabledColor = Color.grey.lighten2 - trackOnDisabledColor = Color.grey.lighten2 - buttonOffDisabledColor = Color.grey.lighten2 - trackOffDisabledColor = Color.grey.lighten2 - case .dark: - buttonOnColor = Color.blue.lighten1 - trackOnColor = Color.blue.lighten2.withAlphaComponent(0.5) - buttonOffColor = Color.grey.lighten2 - trackOffColor = Color.blueGrey.lighten4.withAlphaComponent(0.5) - buttonOnDisabledColor = Color.grey.darken3 - trackOnDisabledColor = Color.grey.lighten1.withAlphaComponent(0.2) - buttonOffDisabledColor = Color.grey.darken3 - trackOffDisabledColor = Color.grey.lighten1.withAlphaComponent(0.2) - } - } - } - /// Switch size. open var switchSize = SwitchSize.medium { didSet { @@ -293,7 +267,6 @@ open class Switch: UIControl { super.init(frame: .zero) prepare() prepareSwitchState(state: state) - prepareSwitchStyle(style: style) prepareSwitchSize(size: size) } @@ -356,8 +329,20 @@ open class Switch: UIControl { prepareTrack() prepareButton() prepareSwitchState() - prepareSwitchStyle() prepareSwitchSize() + applyCurrentTheme() + } + + open func apply(theme: Theme) { + buttonOnColor = theme.secondary + trackOnColor = theme.secondary.withAlphaComponent(0.60) + buttonOffColor = theme.surface.blend(with: theme.onSurface.withAlphaComponent(0.15).blend(with: theme.secondary.withAlphaComponent(0.06))) + trackOffColor = theme.onSurface.withAlphaComponent(0.12) + + buttonOnDisabledColor = theme.surface.blend(with: theme.onSurface.withAlphaComponent(0.15)) + trackOnDisabledColor = theme.onSurface.withAlphaComponent(0.15) + buttonOffDisabledColor = buttonOnDisabledColor + trackOffDisabledColor = trackOnDisabledColor } } @@ -564,15 +549,6 @@ fileprivate extension Switch { updateSwitchState(state: state, animated: false, isTriggeredByUserInteraction: false) } - /** - Prepares the switchStyle property. This is used mainly to allow - init to set the state value and have an effect. - - Parameter style: The SwitchStyle to set. - */ - func prepareSwitchStyle(style: SwitchStyle = .light) { - switchStyle = style - } - /** Prepares the switchSize property. This is used mainly to allow init to set the size value and have an effect. From 656c5e23dba707dce23eb279daa84f50392b8624 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Tue, 9 Oct 2018 19:03:21 +0400 Subject: [PATCH 19/36] Added missing surface color for dark theme --- Sources/iOS/Theme.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index ac0d146a2..eb6a4f330 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -91,6 +91,7 @@ public extension Theme { t.primary = UIColor(rgb: 0x202020) t.secondary = Color.teal.base t.background = UIColor(rgb: 0x303030) + t.surface = t.background t.onBackground = .white t.onSurface = .white return t From 5cbdd8f2a3de2ecb903b5cea86f24606971bfc4f Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Tue, 9 Oct 2018 22:27:03 +0400 Subject: [PATCH 20/36] Made TabsController themeable --- Sources/iOS/TabsController.swift | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Sources/iOS/TabsController.swift b/Sources/iOS/TabsController.swift index 682a4c6cd..3cd652f32 100644 --- a/Sources/iOS/TabsController.swift +++ b/Sources/iOS/TabsController.swift @@ -39,6 +39,12 @@ public enum TabBarAlignment: Int { case bottom } +public enum TabBarThemingStyle { + case auto + case primary + case secondary +} + extension UIViewController { /// TabItem reference. @objc @@ -165,6 +171,9 @@ open class TabsController: TransitionController { } } + /// The tabBar theming style. + open var tabBarThemingStyle = TabBarThemingStyle.auto + /** A UIPanGestureRecognizer property internally used for the interactive swipe. @@ -218,11 +227,55 @@ open class TabsController: TransitionController { prepareTabBar() prepareTabItems() prepareSelectedIndexViewController() + applyCurrentTheme() } open override func transition(to viewController: UIViewController, completion: ((Bool) -> Void)?) { transition(to: viewController, isTriggeredByUserInteraction: false, completion: completion) } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + switch tabBarThemingStyle { + case .auto where parent is NavigationController && tabBarAlignment == .top: + fallthrough + + case .primary: + applyPrimary(theme: theme) + + default: + applySecondary(theme: theme) + } + } +} + +private extension TabsController { + /** + Applies theming taking primary color as base. + - Parameter theme: A Theme + */ + func applyPrimary(theme: Theme) { + tabBar.lineColor = theme.onPrimary.withAlphaComponent(0.68) + tabBar.backgroundColor = theme.primary + tabBar.setTabItemsColor(theme.onPrimary, for: .normal) + tabBar.setTabItemsColor(theme.onPrimary, for: .selected) + tabBar.setTabItemsColor(theme.onPrimary, for: .highlighted) + tabBar.isDividerHidden = true + } + + /** + Applies theming taking secondary color as base. + - Parameter theme: A Theme + */ + func applySecondary(theme: Theme) { + tabBar.lineColor = theme.secondary + tabBar.backgroundColor = theme.background + tabBar.setTabItemsColor(theme.onSurface.withAlphaComponent(0.60), for: .normal) + tabBar.setTabItemsColor(theme.secondary, for: .selected) + tabBar.setTabItemsColor(theme.secondary, for: .highlighted) + tabBar.dividerColor = theme.onSurface.withAlphaComponent(0.12) + } } fileprivate extension TabsController { From b0c3c05532d9aac787884b31ea97a108893b6e28 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Tue, 9 Oct 2018 22:28:31 +0400 Subject: [PATCH 21/36] Fixed tabs controller divider alignment --- Sources/iOS/TabsController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/iOS/TabsController.swift b/Sources/iOS/TabsController.swift index 3cd652f32..c7ad45915 100644 --- a/Sources/iOS/TabsController.swift +++ b/Sources/iOS/TabsController.swift @@ -327,6 +327,7 @@ fileprivate extension TabsController { /// Prepares the TabBar. func prepareTabBar() { tabBar.lineAlignment = .bottom == tabBarAlignment ? .top : .bottom + tabBar.dividerAlignment = .bottom == tabBarAlignment ? .top : .bottom tabBar._delegate = self view.addSubview(tabBar) } From ee9830258cdf1ac3fe460a993f7682b287c16726 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Wed, 10 Oct 2018 17:34:55 +0400 Subject: [PATCH 22/36] Fixed tabBar line was still animated with other transitions --- Sources/iOS/TabBar.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/iOS/TabBar.swift b/Sources/iOS/TabBar.swift index 1346f1a9f..a4b681d8b 100644 --- a/Sources/iOS/TabBar.swift +++ b/Sources/iOS/TabBar.swift @@ -614,6 +614,7 @@ internal extension TabBar { */ func finishLineTransition(isAnimated: Bool = true) { line.motionViewTransition.finish(isAnimated: isAnimated) + line.transition([]) } /** @@ -622,6 +623,7 @@ internal extension TabBar { */ func cancelLineTransition(isAnimated: Bool = true) { line.motionViewTransition.cancel(isAnimated: isAnimated) + line.transition([]) } } From 0e02f6ca10c1976983de4dd8b717b3182b50c36e Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Wed, 10 Oct 2018 17:35:32 +0400 Subject: [PATCH 23/36] Removed SwitchStyle --- Sources/iOS/Switch.swift | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Sources/iOS/Switch.swift b/Sources/iOS/Switch.swift index bca255e7d..7db8013a7 100644 --- a/Sources/iOS/Switch.swift +++ b/Sources/iOS/Switch.swift @@ -30,12 +30,6 @@ import UIKit -@objc(SwitchStyle) -public enum SwitchStyle: Int { - case light - case dark -} - @objc(SwitchState) public enum SwitchState: Int { case on @@ -261,7 +255,7 @@ open class Switch: UIControl, Themeable { - Parameter style: A SwitchStyle value. - Parameter size: A SwitchSize value. */ - public init(state: SwitchState = .off, style: SwitchStyle = .dark, size: SwitchSize = .medium) { + public init(state: SwitchState = .off, size: SwitchSize = .medium) { track = UIView() button = FABButton() super.init(frame: .zero) From f3398a3b9492f756a6ec1c961b569b7742310dda Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Wed, 10 Oct 2018 18:57:11 +0400 Subject: [PATCH 24/36] Made TextView and Editor Themeable --- Sources/iOS/Editor.swift | 16 +++++++++++++++- Sources/iOS/TextView.swift | 8 +++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Sources/iOS/Editor.swift b/Sources/iOS/Editor.swift index a656065a7..b4f8f175c 100644 --- a/Sources/iOS/Editor.swift +++ b/Sources/iOS/Editor.swift @@ -35,7 +35,7 @@ public enum EditorPlaceholderAnimation { case hidden } -open class Editor: View { +open class Editor: View, Themeable { /// Reference to textView. public let textView = TextView() @@ -173,11 +173,14 @@ open class Editor: View { open override func prepare() { super.prepare() + backgroundColor = nil prepareDivider() prepareTextView() preparePlaceholderLabel() prepareDetailLabel() prepareNotificationHandlers() + + applyCurrentTheme() } open override func layoutSubviews() { @@ -187,6 +190,17 @@ open class Editor: View { layoutBottomLabel(label: detailLabel, verticalOffset: detailVerticalOffset) } + open func apply(theme: Theme) { + placeholderActiveColor = theme.secondary + placeholderNormalColor = theme.onSurface.withAlphaComponent(0.38) + + dividerActiveColor = theme.secondary + dividerNormalColor = theme.onSurface.withAlphaComponent(0.12) + + detailColor = theme.onSurface.withAlphaComponent(0.38) + textView.tintColor = theme.secondary + } + @discardableResult open override func becomeFirstResponder() -> Bool { return textView.becomeFirstResponder() diff --git a/Sources/iOS/TextView.swift b/Sources/iOS/TextView.swift index 457c8b577..51b1ead29 100644 --- a/Sources/iOS/TextView.swift +++ b/Sources/iOS/TextView.swift @@ -87,7 +87,7 @@ public protocol TextViewDelegate : UITextViewDelegate { optional func textView(textView: TextView, didProcessEditing textStorage: TextStorage, text: String, range: NSRange) } -open class TextView: UITextView { +open class TextView: UITextView, Themeable { /// A boolean indicating whether the text is empty. open var isEmpty: Bool { return 0 == text?.utf16.count @@ -289,6 +289,7 @@ open class TextView: UITextView { prepareNotificationHandlers() prepareRegularExpression() preparePlaceholderLabel() + applyCurrentTheme() } open override var contentSize: CGSize { @@ -354,6 +355,11 @@ open class TextView: UITextView { super.paste(sender) fixTypingFont() } + + open func apply(theme: Theme) { + textColor = theme.onSurface.withAlphaComponent(0.87) + placeholderColor = theme.onSurface.withAlphaComponent(0.38) + } } fileprivate extension TextView { From 3c020dc8a658a86bee981529baad623156f4bff2 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Wed, 10 Oct 2018 20:29:55 +0400 Subject: [PATCH 25/36] Made StatusBarController themeable --- Sources/iOS/Material+UIColor.swift | 20 ++++++++++++++++++++ Sources/iOS/StatusBarController.swift | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/Sources/iOS/Material+UIColor.swift b/Sources/iOS/Material+UIColor.swift index 21171fded..c36676199 100644 --- a/Sources/iOS/Material+UIColor.swift +++ b/Sources/iOS/Material+UIColor.swift @@ -98,4 +98,24 @@ internal extension UIColor { return UIColor(red: newR, green: newG, blue: newB, alpha: newA) } + + + func adjustingBrightness(by value: CGFloat) -> UIColor { + var h: CGFloat = 0 + var s: CGFloat = 0 + var b: CGFloat = 0 + var a: CGFloat = 0 + + getHue(&h, saturation: &s, brightness: &b, alpha: &a) + + return UIColor(hue: h, saturation: s, brightness: (b + value).clamp(0, 1), alpha: 1) + } + + var lighter: UIColor { + return adjustingBrightness(by: 0.05) + } + + var darker: UIColor { + return adjustingBrightness(by: -0.05) + } } diff --git a/Sources/iOS/StatusBarController.swift b/Sources/iOS/StatusBarController.swift index 08866bf93..d3e3ac1e8 100644 --- a/Sources/iOS/StatusBarController.swift +++ b/Sources/iOS/StatusBarController.swift @@ -120,6 +120,12 @@ open class StatusBarController: TransitionController { super.prepare() prepareStatusBar() } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + statusBar.backgroundColor = theme.primary.darker + } } fileprivate extension StatusBarController { From 0c2955e1d2a5a4285591ba1215918ba09bdb4733 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Wed, 10 Oct 2018 20:30:12 +0400 Subject: [PATCH 26/36] Made ToolbarController themeable --- Sources/iOS/ToolbarController.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Sources/iOS/ToolbarController.swift b/Sources/iOS/ToolbarController.swift index 6c163e519..4b2c31647 100644 --- a/Sources/iOS/ToolbarController.swift +++ b/Sources/iOS/ToolbarController.swift @@ -73,6 +73,24 @@ open class ToolbarController: StatusBarController { prepareToolbar() } + + open override func apply(theme: Theme) { + super.apply(theme: theme) + + toolbar.backgroundColor = theme.primary + toolbar.titleLabel.textColor = theme.onPrimary + toolbar.detailLabel.textColor = theme.onPrimary + + (toolbar.leftViews + toolbar.rightViews + toolbar.centerViews).compactMap { + $0 as? IconButton + }.filter { + $0.isThemingEnabled + }.forEach { + $0.tintColor = theme.onPrimary + $0.titleColor = theme.onPrimary + $0.pulseColor = theme.onPrimary + } + } } fileprivate extension ToolbarController { From 28835e94124d79f24c3e609c1f3712db051e857e Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Wed, 10 Oct 2018 23:35:27 +0400 Subject: [PATCH 27/36] Fixed infinite recursion in Editor --- Sources/iOS/Editor.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Sources/iOS/Editor.swift b/Sources/iOS/Editor.swift index b4f8f175c..8c3d5a1df 100644 --- a/Sources/iOS/Editor.swift +++ b/Sources/iOS/Editor.swift @@ -210,15 +210,6 @@ open class Editor: View, Themeable { open override func resignFirstResponder() -> Bool { return textView.resignFirstResponder() } - - open override var inputAccessoryView: UIView? { - get { - return textView.inputAccessoryView - } - set(value) { - textView.inputAccessoryView = value - } - } } From eae3cc122ff74e8eff6251bcf7bad3e2b2402b0e Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 11 Oct 2018 19:46:11 +0400 Subject: [PATCH 28/36] Reworked toolbar theming --- Sources/iOS/IconButton.swift | 21 +++++++++++-- Sources/iOS/NavigationBar.swift | 15 +-------- Sources/iOS/Toolbar.swift | 47 ++++++++++++++++++++++++++++- Sources/iOS/ToolbarController.swift | 18 ----------- 4 files changed, 66 insertions(+), 35 deletions(-) diff --git a/Sources/iOS/IconButton.swift b/Sources/iOS/IconButton.swift index 92c6bf0d5..2821168c8 100644 --- a/Sources/iOS/IconButton.swift +++ b/Sources/iOS/IconButton.swift @@ -30,7 +30,18 @@ import UIKit +public enum IconButtonThemingStyle { + /// Theming when background content is in background color. + case onBackground + + /// Theming when background content is in primary color. + case onPrimary +} + open class IconButton: Button { + /// A reference to IconButtonThemingStyle. + open var themingStyle = IconButtonThemingStyle.onBackground + open override func prepare() { super.prepare() pulseAnimation = .center @@ -39,7 +50,13 @@ open class IconButton: Button { open override func apply(theme: Theme) { super.apply(theme: theme) - tintColor = theme.secondary - pulseColor = theme.secondary + switch themingStyle { + case .onBackground: + tintColor = theme.secondary + pulseColor = theme.secondary + case .onPrimary: + tintColor = theme.onPrimary + pulseColor = theme.onPrimary + } } } diff --git a/Sources/iOS/NavigationBar.swift b/Sources/iOS/NavigationBar.swift index 7da656299..11e8c8e56 100644 --- a/Sources/iOS/NavigationBar.swift +++ b/Sources/iOS/NavigationBar.swift @@ -179,21 +179,8 @@ open class NavigationBar: UINavigationBar, Themeable { } private func apply(theme: Theme, to item: UINavigationItem) { + Theme.apply(theme: theme, to: item.toolbar) item.toolbar.backgroundColor = .clear - (item.toolbar.leftViews + item.toolbar.rightViews + item.toolbar.centerViews).compactMap { $0 as? IconButton } - .filter { $0.isThemingEnabled } - .forEach { - $0.tintColor = theme.onPrimary - $0.pulseColor = theme.onPrimary - } - - if !((item.titleLabel as? Themeable)?.isThemingEnabled == false) { - item.titleLabel.textColor = theme.onPrimary - } - - if !((item.detailLabel as? Themeable)?.isThemingEnabled == false) { - item.detailLabel.textColor = theme.onPrimary - } } } diff --git a/Sources/iOS/Toolbar.swift b/Sources/iOS/Toolbar.swift index 54ec4feff..c277af50c 100644 --- a/Sources/iOS/Toolbar.swift +++ b/Sources/iOS/Toolbar.swift @@ -30,7 +30,7 @@ import UIKit -open class Toolbar: Bar { +open class Toolbar: Bar, Themeable { /// A convenience property to set the titleLabel.text. @IBInspectable open var title: String? { @@ -63,6 +63,24 @@ open class Toolbar: Bar { @IBInspectable public let detailLabel = UILabel() + open override var leftViews: [UIView] { + didSet { + prepareIconButtons(leftViews) + } + } + + open override var centerViews: [UIView] { + didSet { + prepareIconButtons(centerViews) + } + } + + open override var rightViews: [UIView] { + didSet { + prepareIconButtons(rightViews) + } + } + /** An initializer that initializes the object with a NSCoder object. - Parameter aDecoder: A NSCoder instance. @@ -129,6 +147,25 @@ open class Toolbar: Bar { prepareDetailLabel() } + open func apply(theme: Theme) { + backgroundColor = theme.primary + (leftViews + rightViews + centerViews).forEach { + guard let v = $0 as? IconButton, v.isThemingEnabled else { + return + } + + v.apply(theme: theme) + } + + if !((titleLabel as? Themeable)?.isThemingEnabled == false) { + titleLabel.textColor = theme.onPrimary + } + + if !((detailLabel as? Themeable)?.isThemingEnabled == false) { + detailLabel.textColor = theme.onPrimary + } + } + /// A reference to titleLabel.textAlignment observation. private var titleLabelTextAlignmentObserver: NSKeyValueObservation! } @@ -152,4 +189,12 @@ private extension Toolbar { detailLabel.font = RobotoFont.regular(with: 12) detailLabel.textColor = Color.darkText.secondary } + + func prepareIconButtons(_ views: [UIView]) { + views.forEach { + ($0 as? IconButton)?.themingStyle = .onPrimary + } + + applyCurrentTheme() + } } diff --git a/Sources/iOS/ToolbarController.swift b/Sources/iOS/ToolbarController.swift index 4b2c31647..6c163e519 100644 --- a/Sources/iOS/ToolbarController.swift +++ b/Sources/iOS/ToolbarController.swift @@ -73,24 +73,6 @@ open class ToolbarController: StatusBarController { prepareToolbar() } - - open override func apply(theme: Theme) { - super.apply(theme: theme) - - toolbar.backgroundColor = theme.primary - toolbar.titleLabel.textColor = theme.onPrimary - toolbar.detailLabel.textColor = theme.onPrimary - - (toolbar.leftViews + toolbar.rightViews + toolbar.centerViews).compactMap { - $0 as? IconButton - }.filter { - $0.isThemingEnabled - }.forEach { - $0.tintColor = theme.onPrimary - $0.titleColor = theme.onPrimary - $0.pulseColor = theme.onPrimary - } - } } fileprivate extension ToolbarController { From a6869cbc181453a9662c08fb2f193c70307f96a4 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 11 Oct 2018 20:34:08 +0400 Subject: [PATCH 29/36] Made BottomNavigationController themeable --- Sources/iOS/BottomNavigationController.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Sources/iOS/BottomNavigationController.swift b/Sources/iOS/BottomNavigationController.swift index 38d926f92..d82caf0c8 100644 --- a/Sources/iOS/BottomNavigationController.swift +++ b/Sources/iOS/BottomNavigationController.swift @@ -51,7 +51,7 @@ private class MaterialTabBar: UITabBar { } } -open class BottomNavigationController: UITabBarController { +open class BottomNavigationController: UITabBarController, Themeable { /// A Boolean that controls if the swipe feature is enabled. open var isSwipeEnabled = true { didSet { @@ -168,6 +168,17 @@ open class BottomNavigationController: UITabBarController { prepareTabBar() isSwipeEnabled = true isMotionEnabled = true + applyCurrentTheme() + } + + open func apply(theme: Theme) { + tabBar.tintColor = theme.secondary + tabBar.barTintColor = theme.background + tabBar.dividerColor = theme.onSurface.withAlphaComponent(0.12) + + if #available(iOS 10.0, *) { + tabBar.unselectedItemTintColor = theme.onSurface.withAlphaComponent(0.54) + } } } From a217eaa1bc5b52aa52f335dedc8b8c3b95205993 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 11 Oct 2018 20:51:06 +0400 Subject: [PATCH 30/36] Fixed theming override issue --- Sources/iOS/Theme.swift | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index eb6a4f330..58b946543 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -118,7 +118,7 @@ public extension Theme { - Parameter to view: A UIView. */ static func apply(theme: Theme, to view: UIView) { - guard !((view as? Themeable)?.isThemingEnabled == false) else { + guard !((view as? Themeable)?.isThemingEnabled == false), !view.isProcessed else { return } @@ -141,9 +141,15 @@ public extension Theme { viewController.children.forEach { apply(theme: theme, to: $0) + $0.view.isProcessed = true } apply(theme: theme, to: viewController.view) + + viewController.children.forEach { + $0.view.isProcessed = false + } + (viewController as? Themeable)?.apply(theme: theme) } @@ -187,3 +193,20 @@ public extension Themeable where Self: NSObject { apply(theme: .current) } } + +/// A memory reference to the isProcessed for UIView. +private var IsProcessedKey: UInt8 = 0 + +private extension UIView { + /// A boolean indicating if view is already themed. + var isProcessed: Bool { + get { + return AssociatedObject.get(base: self, key: &IsProcessedKey) { + false + } + } + set(value) { + AssociatedObject.set(base: self, key: &IsProcessedKey, value: value) + } + } +} From 5830b0992e6f9f1bf7f7176d73e8b4d72ced2d79 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 11 Oct 2018 21:01:10 +0400 Subject: [PATCH 31/36] Fixed some view controllers not themed in the hierarchy --- Sources/iOS/Theme.swift | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index 58b946543..6e41b8200 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -139,14 +139,14 @@ public extension Theme { return } - viewController.children.forEach { + viewController.allChildren.forEach { apply(theme: theme, to: $0) $0.view.isProcessed = true } apply(theme: theme, to: viewController.view) - viewController.children.forEach { + viewController.allChildren.forEach { $0.view.isProcessed = false } @@ -194,6 +194,23 @@ public extension Themeable where Self: NSObject { } } +private extension UIViewController { + /// Returns all possible child view controllers. + var allChildren: [UIViewController] { + var all = children + + if let v = self as? TabsController { + all += v.viewControllers + } + + if let v = presentedViewController, v.presentingViewController === self { + all.append(v) + } + + return all + } +} + /// A memory reference to the isProcessed for UIView. private var IsProcessedKey: UInt8 = 0 From c7f75ba088d668e910c04afed3f1b1db331bf776 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 11 Oct 2018 21:02:44 +0400 Subject: [PATCH 32/36] Made Theme Hashable --- Sources/iOS/Theme.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/iOS/Theme.swift b/Sources/iOS/Theme.swift index 6e41b8200..447152930 100644 --- a/Sources/iOS/Theme.swift +++ b/Sources/iOS/Theme.swift @@ -42,9 +42,9 @@ public protocol Themeable: class { var isThemingEnabled: Bool { get set } } -public struct Theme { +public struct Theme: Hashable { /// The color displayed most frequently across the app. - public var primary = Color.blue.base + public var primary = Color.blue.darken2 /// Accent color for some components such as FABMenu. public var secondary = Color.blue.base From f47bdadf8c34db2b8fe5e5da2fc4f12a4ef2f679 Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 11 Oct 2018 21:18:09 +0400 Subject: [PATCH 33/36] Added missing comments --- Sources/iOS/BottomNavigationController.swift | 4 ++++ Sources/iOS/Button.swift | 4 ++-- Sources/iOS/Editor.swift | 4 ++++ Sources/iOS/Material+UIColor.swift | 12 +++++++++--- Sources/iOS/NavigationBar.swift | 9 +++++++++ Sources/iOS/Switch.swift | 4 ++++ Sources/iOS/TextField.swift | 4 ++++ Sources/iOS/TextView.swift | 4 ++++ Sources/iOS/Toolbar.swift | 4 ++++ 9 files changed, 44 insertions(+), 5 deletions(-) diff --git a/Sources/iOS/BottomNavigationController.swift b/Sources/iOS/BottomNavigationController.swift index d82caf0c8..48eb81318 100644 --- a/Sources/iOS/BottomNavigationController.swift +++ b/Sources/iOS/BottomNavigationController.swift @@ -171,6 +171,10 @@ open class BottomNavigationController: UITabBarController, Themeable { applyCurrentTheme() } + /** + Applies the given theme. + - Parameter theme: A Theme. + */ open func apply(theme: Theme) { tabBar.tintColor = theme.secondary tabBar.barTintColor = theme.background diff --git a/Sources/iOS/Button.swift b/Sources/iOS/Button.swift index 296745354..e18651d4d 100644 --- a/Sources/iOS/Button.swift +++ b/Sources/iOS/Button.swift @@ -285,9 +285,9 @@ open class Button: UIButton, Pulseable, PulseableLayer, Themeable { } /** - Applies given theme to the view. + Applies the given theme. - Parameter theme: A Theme. - */ + */ open func apply(theme: Theme) { } } diff --git a/Sources/iOS/Editor.swift b/Sources/iOS/Editor.swift index 8c3d5a1df..e74c29bf7 100644 --- a/Sources/iOS/Editor.swift +++ b/Sources/iOS/Editor.swift @@ -190,6 +190,10 @@ open class Editor: View, Themeable { layoutBottomLabel(label: detailLabel, verticalOffset: detailVerticalOffset) } + /** + Applies the given theme. + - Parameter theme: A Theme. + */ open func apply(theme: Theme) { placeholderActiveColor = theme.secondary placeholderNormalColor = theme.onSurface.withAlphaComponent(0.38) diff --git a/Sources/iOS/Material+UIColor.swift b/Sources/iOS/Material+UIColor.swift index c36676199..5d300f45d 100644 --- a/Sources/iOS/Material+UIColor.swift +++ b/Sources/iOS/Material+UIColor.swift @@ -99,7 +99,11 @@ internal extension UIColor { return UIColor(red: newR, green: newG, blue: newB, alpha: newA) } - + /** + Adjusts brightness of the color by given value. + - Parameter by value: A CGFloat value. + - Returns: Adjusted color. + */ func adjustingBrightness(by value: CGFloat) -> UIColor { var h: CGFloat = 0 var s: CGFloat = 0 @@ -111,11 +115,13 @@ internal extension UIColor { return UIColor(hue: h, saturation: s, brightness: (b + value).clamp(0, 1), alpha: 1) } + /// A lighter version of the color. var lighter: UIColor { - return adjustingBrightness(by: 0.05) + return adjustingBrightness(by: 0.1) } + /// A darker version of the color. var darker: UIColor { - return adjustingBrightness(by: -0.05) + return adjustingBrightness(by: -0.1) } } diff --git a/Sources/iOS/NavigationBar.swift b/Sources/iOS/NavigationBar.swift index 11e8c8e56..f28f32a76 100644 --- a/Sources/iOS/NavigationBar.swift +++ b/Sources/iOS/NavigationBar.swift @@ -171,6 +171,10 @@ open class NavigationBar: UINavigationBar, Themeable { applyCurrentTheme() } + /** + Applies the given theme. + - Parameter theme: A Theme. + */ open func apply(theme: Theme) { backgroundColor = theme.primary items?.forEach { @@ -178,6 +182,11 @@ open class NavigationBar: UINavigationBar, Themeable { } } + /** + Applies the given theme to the navigation item. + - Parameter theme: A Theme. + - Parameter to item: A UINavigationItem. + */ private func apply(theme: Theme, to item: UINavigationItem) { Theme.apply(theme: theme, to: item.toolbar) item.toolbar.backgroundColor = .clear diff --git a/Sources/iOS/Switch.swift b/Sources/iOS/Switch.swift index 7db8013a7..dcce9ae32 100644 --- a/Sources/iOS/Switch.swift +++ b/Sources/iOS/Switch.swift @@ -327,6 +327,10 @@ open class Switch: UIControl, Themeable { applyCurrentTheme() } + /** + Applies the given theme. + - Parameter theme: A Theme. + */ open func apply(theme: Theme) { buttonOnColor = theme.secondary trackOnColor = theme.secondary.withAlphaComponent(0.60) diff --git a/Sources/iOS/TextField.swift b/Sources/iOS/TextField.swift index b5dbf1433..ccb11e06c 100644 --- a/Sources/iOS/TextField.swift +++ b/Sources/iOS/TextField.swift @@ -462,6 +462,10 @@ open class TextField: UITextField, Themeable { applyCurrentTheme() } + /** + Applies the given theme. + - Parameter theme: A Theme. + */ open func apply(theme: Theme) { placeholderActiveColor = theme.secondary placeholderNormalColor = theme.onSurface.withAlphaComponent(0.38) diff --git a/Sources/iOS/TextView.swift b/Sources/iOS/TextView.swift index 51b1ead29..ecfdbff07 100644 --- a/Sources/iOS/TextView.swift +++ b/Sources/iOS/TextView.swift @@ -356,6 +356,10 @@ open class TextView: UITextView, Themeable { fixTypingFont() } + /** + Applies the given theme. + - Parameter theme: A Theme. + */ open func apply(theme: Theme) { textColor = theme.onSurface.withAlphaComponent(0.87) placeholderColor = theme.onSurface.withAlphaComponent(0.38) diff --git a/Sources/iOS/Toolbar.swift b/Sources/iOS/Toolbar.swift index c277af50c..ed27796cd 100644 --- a/Sources/iOS/Toolbar.swift +++ b/Sources/iOS/Toolbar.swift @@ -147,6 +147,10 @@ open class Toolbar: Bar, Themeable { prepareDetailLabel() } + /** + Applies the given theme. + - Parameter theme: A Theme. + */ open func apply(theme: Theme) { backgroundColor = theme.primary (leftViews + rightViews + centerViews).forEach { From 0b14b2a53460885d33b457532627452107f94bdb Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 11 Oct 2018 21:42:23 +0400 Subject: [PATCH 34/36] Added detecting if TabsController is under ToolbarController --- Sources/iOS/TabsController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/iOS/TabsController.swift b/Sources/iOS/TabsController.swift index c7ad45915..b48f0ad64 100644 --- a/Sources/iOS/TabsController.swift +++ b/Sources/iOS/TabsController.swift @@ -238,7 +238,7 @@ open class TabsController: TransitionController { super.apply(theme: theme) switch tabBarThemingStyle { - case .auto where parent is NavigationController && tabBarAlignment == .top: + case .auto where (parent is NavigationController || parent is ToolbarController) && tabBarAlignment == .top: fallthrough case .primary: From ded81b488e67c88d40729ab22d7fdd1d4c0f96cf Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Thu, 11 Oct 2018 21:45:33 +0400 Subject: [PATCH 35/36] Fixed setting tabBarAlignment does not update line/divider alignment --- Sources/iOS/TabsController.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Sources/iOS/TabsController.swift b/Sources/iOS/TabsController.swift index b48f0ad64..0c539f4a0 100644 --- a/Sources/iOS/TabsController.swift +++ b/Sources/iOS/TabsController.swift @@ -167,6 +167,7 @@ open class TabsController: TransitionController { /// The tabBar alignment. open var tabBarAlignment = TabBarAlignment.bottom { didSet { + updateTabBarAlignment() layoutSubviews() } } @@ -326,10 +327,14 @@ fileprivate extension TabsController { /// Prepares the TabBar. func prepareTabBar() { - tabBar.lineAlignment = .bottom == tabBarAlignment ? .top : .bottom - tabBar.dividerAlignment = .bottom == tabBarAlignment ? .top : .bottom tabBar._delegate = self view.addSubview(tabBar) + updateTabBarAlignment() + } + + func updateTabBarAlignment() { + tabBar.lineAlignment = .bottom == tabBarAlignment ? .top : .bottom + tabBar.dividerAlignment = .bottom == tabBarAlignment ? .top : .bottom } /// Prepares the `tabBar.tabItems`. From 5ee47a0c3a6ec84be21122a8b44843556e596d4f Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Mon, 15 Oct 2018 22:31:02 +0400 Subject: [PATCH 36/36] Gave tintColor and titleColor parameters precedence over theming --- Sources/iOS/Button.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/iOS/Button.swift b/Sources/iOS/Button.swift index e18651d4d..fe0462666 100644 --- a/Sources/iOS/Button.swift +++ b/Sources/iOS/Button.swift @@ -195,8 +195,8 @@ open class Button: UIButton, Pulseable, PulseableLayer, Themeable { */ public init(image: UIImage?, tintColor: UIColor = Color.blue.base) { super.init(frame: .zero) - prepare(with: image, tintColor: tintColor) prepare() + prepare(with: image, tintColor: tintColor) } /** @@ -206,8 +206,8 @@ open class Button: UIButton, Pulseable, PulseableLayer, Themeable { */ public init(title: String?, titleColor: UIColor = Color.blue.base) { super.init(frame: .zero) - prepare(with: title, titleColor: titleColor) prepare() + prepare(with: title, titleColor: titleColor) } open override func layoutSubviews() {