Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing Theming to Material #1115

Merged
merged 36 commits into from
Oct 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
848d5a3
Added Theme
OrkhanAlikhanov Oct 4, 2018
16d1f53
Disallow setting Theme.current directly
OrkhanAlikhanov Oct 4, 2018
ca4a485
Added empty Theme initializer
OrkhanAlikhanov Oct 4, 2018
e9fc6bb
Added applying theme for only components created in a code block
OrkhanAlikhanov Oct 4, 2018
3c21dd3
Added isThemingEnabled
OrkhanAlikhanov Oct 5, 2018
97787b5
Fixed cannot assign to let constant issue for isThemingEnabled
OrkhanAlikhanov Oct 5, 2018
6eff97c
Changed theme application order to `subviews first`
OrkhanAlikhanov Oct 5, 2018
a14404f
Fixed Theme.current not set when there is no rootViewController
OrkhanAlikhanov Oct 5, 2018
c217102
Added applyCurrentTheme for Themeables
OrkhanAlikhanov Oct 6, 2018
b5908d8
Made buttons themeable
OrkhanAlikhanov Oct 6, 2018
c08a143
Made TextFields themeable
OrkhanAlikhanov Oct 6, 2018
85d97b1
Made NavigationBar themeable
OrkhanAlikhanov Oct 6, 2018
8ec40dd
Made ViewController themeable
OrkhanAlikhanov Oct 6, 2018
93e8f1a
Fixed debug view hierarchy not working when we have Check/RadioButton
OrkhanAlikhanov Oct 6, 2018
6a22d0e
Adjusted CheckButton and RadioButton icon
OrkhanAlikhanov Oct 6, 2018
f7ba4c9
Added state-aware pulse coloring for Radio and CheckButton
OrkhanAlikhanov Oct 6, 2018
7cd9489
Added UIColor.blend(with:)
OrkhanAlikhanov Oct 9, 2018
4728c88
Made Switch themeable
OrkhanAlikhanov Oct 9, 2018
656c5e2
Added missing surface color for dark theme
OrkhanAlikhanov Oct 9, 2018
5cbdd8f
Made TabsController themeable
OrkhanAlikhanov Oct 9, 2018
b0c3c05
Fixed tabs controller divider alignment
OrkhanAlikhanov Oct 9, 2018
ee98302
Fixed tabBar line was still animated with other transitions
OrkhanAlikhanov Oct 10, 2018
0e02f6c
Removed SwitchStyle
OrkhanAlikhanov Oct 10, 2018
f3398a3
Made TextView and Editor Themeable
OrkhanAlikhanov Oct 10, 2018
3c020dc
Made StatusBarController themeable
OrkhanAlikhanov Oct 10, 2018
0c2955e
Made ToolbarController themeable
OrkhanAlikhanov Oct 10, 2018
28835e9
Fixed infinite recursion in Editor
OrkhanAlikhanov Oct 10, 2018
eae3cc1
Reworked toolbar theming
OrkhanAlikhanov Oct 11, 2018
a6869cb
Made BottomNavigationController themeable
OrkhanAlikhanov Oct 11, 2018
a217eaa
Fixed theming override issue
OrkhanAlikhanov Oct 11, 2018
5830b09
Fixed some view controllers not themed in the hierarchy
OrkhanAlikhanov Oct 11, 2018
c7f75ba
Made Theme Hashable
OrkhanAlikhanov Oct 11, 2018
f47bdad
Added missing comments
OrkhanAlikhanov Oct 11, 2018
0b14b2a
Added detecting if TabsController is under ToolbarController
OrkhanAlikhanov Oct 11, 2018
ded81b4
Fixed setting tabBarAlignment does not update line/divider alignment
OrkhanAlikhanov Oct 11, 2018
5ee47a0
Gave tintColor and titleColor parameters precedence over theming
OrkhanAlikhanov Oct 15, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Material.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -292,6 +293,7 @@
96E09DC71F2287E50000B121 /* TabsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsController.swift; sourceTree = "<group>"; };
96E3C3931D397AE90086A024 /* Material+UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIView.swift"; sourceTree = "<group>"; };
96F1DC871D654FDF0025F925 /* Material+CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+CALayer.swift"; sourceTree = "<group>"; };
9D00EBB3216675FB00DBCD69 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
9D054A6320D175AC00D0528D /* Material+UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIButton.swift"; sourceTree = "<group>"; };
9D054A6420D175AC00D0528D /* Material+UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UILabel.swift"; sourceTree = "<group>"; };
9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -567,6 +569,7 @@
963FBF011D6696AB008F8512 /* Tab */,
966ECF2B1CF4C21B00BB0BDF /* Table */,
96090B031D9D709E00709CA6 /* Text */,
9D00EBB2216675A800DBCD69 /* Theme */,
963FBF001D66964F008F8512 /* Toolbar */,
9626CA951DAB5370003E2611 /* Transition */,
96BCB8061CB40FD000C806FE /* Type */,
Expand Down Expand Up @@ -760,6 +763,14 @@
name = Animation;
sourceTree = "<group>";
};
9D00EBB2216675A800DBCD69 /* Theme */ = {
isa = PBXGroup;
children = (
9D00EBB3216675FB00DBCD69 /* Theme.swift */,
);
name = Theme;
sourceTree = "<group>";
};
9DE84D6E1FF0250E00586C8B /* ButtonGroup */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -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 */,
Expand Down
42 changes: 29 additions & 13 deletions Sources/iOS/BaseIconLayerButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ open class BaseIconLayerButton: Button {
open override var isSelected: Bool {
didSet {
iconLayer.setSelected(isSelected, animated: false)
updatePulseColor()
}
}

Expand Down Expand Up @@ -84,6 +85,7 @@ open class BaseIconLayerButton: Button {
open override func prepare() {
super.prepare()
layer.addSublayer(iconLayer)
iconLayer.prepare()
contentHorizontalAlignment = .left // default was .center
reloadImage()
}
Expand Down Expand Up @@ -122,7 +124,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()
}
Expand All @@ -136,12 +138,24 @@ 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()
}
}

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)

selectedPulseColor = theme.secondary
normalPulseColor = theme.onSurface
updatePulseColor()
}


/// This might be considered as a hackish way, but it's just manipulation
Expand All @@ -159,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
Expand All @@ -185,16 +211,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
Expand Down
17 changes: 16 additions & 1 deletion Sources/iOS/BottomNavigationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -168,6 +168,21 @@ open class BottomNavigationController: UITabBarController {
prepareTabBar()
isSwipeEnabled = true
isMotionEnabled = true
applyCurrentTheme()
}

/**
Applies the given theme.
- Parameter theme: A Theme.
*/
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)
}
}
}

Expand Down
13 changes: 10 additions & 3 deletions Sources/iOS/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -195,8 +195,8 @@ open class Button: UIButton, Pulseable, PulseableLayer {
*/
public init(image: UIImage?, tintColor: UIColor = Color.blue.base) {
super.init(frame: .zero)
prepare(with: image, tintColor: tintColor)
prepare()
prepare(with: image, tintColor: tintColor)
}

/**
Expand All @@ -206,8 +206,8 @@ open class Button: UIButton, Pulseable, PulseableLayer {
*/
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() {
Expand Down Expand Up @@ -281,7 +281,14 @@ open class Button: UIButton, Pulseable, PulseableLayer {
contentScaleFactor = Screen.scale
prepareVisualLayer()
preparePulse()
applyCurrentTheme()
}

/**
Applies the given theme.
- Parameter theme: A Theme.
*/
open func apply(theme: Theme) { }
}

extension Button {
Expand Down
7 changes: 7 additions & 0 deletions Sources/iOS/CheckButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
29 changes: 19 additions & 10 deletions Sources/iOS/Editor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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() {
Expand All @@ -187,6 +190,21 @@ open class Editor: View {
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)

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()
Expand All @@ -196,15 +214,6 @@ open class Editor: View {
open override func resignFirstResponder() -> Bool {
return textView.resignFirstResponder()
}

open override var inputAccessoryView: UIView? {
get {
return textView.inputAccessoryView
}
set(value) {
textView.inputAccessoryView = value
}
}
}


Expand Down
6 changes: 6 additions & 0 deletions Sources/iOS/ErrorTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
10 changes: 9 additions & 1 deletion Sources/iOS/FABButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
9 changes: 9 additions & 0 deletions Sources/iOS/FlatButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
24 changes: 24 additions & 0 deletions Sources/iOS/IconButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,33 @@

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
}

open override func apply(theme: Theme) {
super.apply(theme: theme)

switch themingStyle {
case .onBackground:
tintColor = theme.secondary
pulseColor = theme.secondary
case .onPrimary:
tintColor = theme.onPrimary
pulseColor = theme.onPrimary
}
}
}
Loading