patternswiftMinor
Storing global styles and utilizing them throughout view controllers
Viewed 0 times
globalutilizingcontrollersviewstylesthroughoutthemandstoring
Problem
Assuming an app has many display styles, fonts, colors, etc., and that we never want to hardcode values, I've created an
```
class AppVariables: NSObject {
/**
* Fonts
*/
let UIFontPrimary10 = UIFont(name: "Heiti TC", size: 10)
let UIFontPrimary12 = UIFont(name: "Heiti TC", size: 12)
let UIFontPrimary14 = UIFont(name: "Heiti TC", size: 14)
var attribsFont10: NSDictionary!
var attribsFont12: NSDictionary!
var attribsFont14: NSDictionary!
var paragraphStyleFont10 = NSMutableParagraphStyle()
var paragraphStyleFont12 = NSMutableParagraphStyle()
var paragraphStyleFont14 = NSMutableParagraphStyle()
/**
* Colors
*/
let UIColorWhiteOff = UIColor(red: 250.0/255.0, green: 250.0/255.0, blue: 250.0/255.0, alpha: 1)
let UIColorPurple = UIColor(red: 134.0/255.0, green: 129.0/255.0, blue: 204.0/255.0, alpha: 1)
let UIColorPurpleLight = UIColor(red: 178.0/255.0, green: 176.0/255.0, blue: 217.0/255.0, alpha: 1)
let UIColorBlueDark = UIColor(red: 40.0/255.0, green: 35.0/255.0, blue: 61.0/255.0, alpha: 1)
let UIColorBlueDarkAlpha6 = UIColor(red: 40.0/255.0, green: 35.0/255.0, blue: 61.0/255.0, alpha: 0.6)
let UIColorBlueDarkAlpha95 = UIColor(red: 40.0/255.0, green: 35.0/255.0, blue: 61.0/255.0, alpha: 0.95)
/**
* Dimensions and margins
*/
var screenWidth: CGFloat!
var screenHeight: CGFloat!
var viewPadding: CGFloat!
var viewPaddingSmall: CGFloat!
var viewWidth: CGFloat!
init() {
super.init()
self.paragraphStyleFont10.lineSpacing = 4
self.paragraphStyleFont12.lineSpacing = 5
self.paragraphStyleFont14.lineSpacing = 6
self.attribsFont10 = [NSFontAttributeName: self.UIFontPrimary10, NSParagraphStyleAttributeName: self.paragraphStyleFont10]
self.attribsFont12 = [NSFontAttributeName: self.UIFontPrimary12, NSParagraphStyleAtt
AppVariables object that houses properties for all of the necessary styles in my app:```
class AppVariables: NSObject {
/**
* Fonts
*/
let UIFontPrimary10 = UIFont(name: "Heiti TC", size: 10)
let UIFontPrimary12 = UIFont(name: "Heiti TC", size: 12)
let UIFontPrimary14 = UIFont(name: "Heiti TC", size: 14)
var attribsFont10: NSDictionary!
var attribsFont12: NSDictionary!
var attribsFont14: NSDictionary!
var paragraphStyleFont10 = NSMutableParagraphStyle()
var paragraphStyleFont12 = NSMutableParagraphStyle()
var paragraphStyleFont14 = NSMutableParagraphStyle()
/**
* Colors
*/
let UIColorWhiteOff = UIColor(red: 250.0/255.0, green: 250.0/255.0, blue: 250.0/255.0, alpha: 1)
let UIColorPurple = UIColor(red: 134.0/255.0, green: 129.0/255.0, blue: 204.0/255.0, alpha: 1)
let UIColorPurpleLight = UIColor(red: 178.0/255.0, green: 176.0/255.0, blue: 217.0/255.0, alpha: 1)
let UIColorBlueDark = UIColor(red: 40.0/255.0, green: 35.0/255.0, blue: 61.0/255.0, alpha: 1)
let UIColorBlueDarkAlpha6 = UIColor(red: 40.0/255.0, green: 35.0/255.0, blue: 61.0/255.0, alpha: 0.6)
let UIColorBlueDarkAlpha95 = UIColor(red: 40.0/255.0, green: 35.0/255.0, blue: 61.0/255.0, alpha: 0.95)
/**
* Dimensions and margins
*/
var screenWidth: CGFloat!
var screenHeight: CGFloat!
var viewPadding: CGFloat!
var viewPaddingSmall: CGFloat!
var viewWidth: CGFloat!
init() {
super.init()
self.paragraphStyleFont10.lineSpacing = 4
self.paragraphStyleFont12.lineSpacing = 5
self.paragraphStyleFont14.lineSpacing = 6
self.attribsFont10 = [NSFontAttributeName: self.UIFontPrimary10, NSParagraphStyleAttributeName: self.paragraphStyleFont10]
self.attribsFont12 = [NSFontAttributeName: self.UIFontPrimary12, NSParagraphStyleAtt
Solution
-
I think that you would be better off using static methods on a struct or class. This removes the need for you to access your style through the App Delegate. The App Delegate should only be concerned with handling the delegate callbacks from the OS, storing globally accessible values in it messes with the principle of separation of concerns and creates a spider web of dependencies hurting the reusability and understandability of your code.
-
Lumping Fonts, Colors, Dimensions, Paragraph styles, etc. all into a single level is not very discoverable. I think it would be better to separate them into their own types.
-
In my experience a single color is not enough to define a style. Colors almost always come associated with other colors like foreground v.s. background, or normal v.s. highlighted.
-
It doesn't make sense to create multiple constants for a single font of different sizes. Better to have a method that takes in a size and returns the special font with the given size.
I recently started creating a collection of "style" classes for myself. This is what I did for colors:
I can then get a color from a style like so:
This also allowed me to create subclasses of common UIKit components that allow me to simply set a StyleColor on them to fully configure them to a style:
```
class StyleButton: UIButton {
var style: StyleColor = StyleColor() {
didSet {
self._configureView()
}
}
init(coder aDecoder: NSCoder!, style : StyleColor) {
self.style = style
super.init(coder: aDecoder)
self._configureView()
}
init() {
super.init(frame: CGRect())
self._configureView()
}
func _configureView() {
self.showsTouchWhenHighlighted = false
self.adjustsImageWhenHighlighted = false
self.adjustsImageWhenDisabled = false
self.reversesTitleShadowWhenHighlighted = false
self.setTitleColor(style.color(.Normal, .Background), forState: .Normal)
self.set
I think that you would be better off using static methods on a struct or class. This removes the need for you to access your style through the App Delegate. The App Delegate should only be concerned with handling the delegate callbacks from the OS, storing globally accessible values in it messes with the principle of separation of concerns and creates a spider web of dependencies hurting the reusability and understandability of your code.
-
Lumping Fonts, Colors, Dimensions, Paragraph styles, etc. all into a single level is not very discoverable. I think it would be better to separate them into their own types.
-
In my experience a single color is not enough to define a style. Colors almost always come associated with other colors like foreground v.s. background, or normal v.s. highlighted.
-
It doesn't make sense to create multiple constants for a single font of different sizes. Better to have a method that takes in a size and returns the special font with the given size.
I recently started creating a collection of "style" classes for myself. This is what I did for colors:
extension UIColor {
convenience init(hex : Int) {
let blue = CGFloat(hex & 0xFF)
let green = CGFloat((hex >> 8) & 0xFF)
let red = CGFloat((hex >> 16) & 0xFF)
self.init(red: red / 255.0, green: green / 255.0, blue: blue / 255.0, alpha: 1)
}
}
struct DepthColor {
enum Depth {
case Foreground, Background
}
let foreground: UIColor
let background: UIColor
init(foreground: Int, background: Int) {
self.foreground = UIColor(hex: foreground)
self.background = UIColor(hex: background)
}
init() {
self.foreground = UIColor(hex: 0xFFFFFF)
self.background = UIColor(hex: 0)
}
func colorForDepth(depth : Depth) -> UIColor {
switch(depth) {
case .Foreground:
return self.foreground
case .Background:
return self.background
}
}
}
struct StyleColor {
enum Purpose {
case Normal, Highlighted, Disabled
}
let normal : DepthColor
let highlighted : DepthColor
let disabled : DepthColor
init(normal: DepthColor = DepthColor(), highlighted: DepthColor = DepthColor(), disabled: DepthColor = DepthColor()) {
self.normal = normal
self.highlighted = highlighted
self.disabled = disabled
}
func depthColorForPurpose(purpose : Purpose) -> DepthColor {
switch(purpose) {
case .Normal:
return self.normal
case .Highlighted:
return self.highlighted
case .Disabled:
return self.disabled
}
}
func color(_ purpose : Purpose = .Normal, _ depth: DepthColor.Depth = .Foreground) -> UIColor {
return self.depthColorForPurpose(purpose).colorForDepth(depth)
}
static func positiveAction() -> StyleColor {
return StyleColor(
normal: DepthColor(foreground: 0x45A332, background: 0xFFFFFF),
highlighted: DepthColor(foreground: 0x45A332, background: 0xFFFFFF),
disabled: DepthColor(foreground: 0x888888, background: 0xFFFFFF)
)
}
static func deleteAction() -> StyleColor {
return StyleColor(
normal: DepthColor(foreground: 0xF31717, background: 0xFFFFFF),
highlighted: DepthColor(foreground: 0xD91414, background: 0xFFFFFF),
disabled: DepthColor(foreground: 0x888888, background: 0xFFFFFF)
)
}
static func neutralAction() -> StyleColor {
return StyleColor(
normal: DepthColor(foreground: 0x666666, background: 0xFFFFFF),
highlighted: DepthColor(foreground: 0x555555, background: 0xFFFFFF),
disabled: DepthColor(foreground: 0x888888, background: 0xFFFFFF)
)
}
}I can then get a color from a style like so:
var color = StyleColor.positiveAction().color()
color = StyleColor.positiveAction().color(.Highlighted)
color = StyleColor.positiveAction().color(.Highlighted, .Background)This also allowed me to create subclasses of common UIKit components that allow me to simply set a StyleColor on them to fully configure them to a style:
```
class StyleButton: UIButton {
var style: StyleColor = StyleColor() {
didSet {
self._configureView()
}
}
init(coder aDecoder: NSCoder!, style : StyleColor) {
self.style = style
super.init(coder: aDecoder)
self._configureView()
}
init() {
super.init(frame: CGRect())
self._configureView()
}
func _configureView() {
self.showsTouchWhenHighlighted = false
self.adjustsImageWhenHighlighted = false
self.adjustsImageWhenDisabled = false
self.reversesTitleShadowWhenHighlighted = false
self.setTitleColor(style.color(.Normal, .Background), forState: .Normal)
self.set
Code Snippets
extension UIColor {
convenience init(hex : Int) {
let blue = CGFloat(hex & 0xFF)
let green = CGFloat((hex >> 8) & 0xFF)
let red = CGFloat((hex >> 16) & 0xFF)
self.init(red: red / 255.0, green: green / 255.0, blue: blue / 255.0, alpha: 1)
}
}
struct DepthColor {
enum Depth {
case Foreground, Background
}
let foreground: UIColor
let background: UIColor
init(foreground: Int, background: Int) {
self.foreground = UIColor(hex: foreground)
self.background = UIColor(hex: background)
}
init() {
self.foreground = UIColor(hex: 0xFFFFFF)
self.background = UIColor(hex: 0)
}
func colorForDepth(depth : Depth) -> UIColor {
switch(depth) {
case .Foreground:
return self.foreground
case .Background:
return self.background
}
}
}
struct StyleColor {
enum Purpose {
case Normal, Highlighted, Disabled
}
let normal : DepthColor
let highlighted : DepthColor
let disabled : DepthColor
init(normal: DepthColor = DepthColor(), highlighted: DepthColor = DepthColor(), disabled: DepthColor = DepthColor()) {
self.normal = normal
self.highlighted = highlighted
self.disabled = disabled
}
func depthColorForPurpose(purpose : Purpose) -> DepthColor {
switch(purpose) {
case .Normal:
return self.normal
case .Highlighted:
return self.highlighted
case .Disabled:
return self.disabled
}
}
func color(_ purpose : Purpose = .Normal, _ depth: DepthColor.Depth = .Foreground) -> UIColor {
return self.depthColorForPurpose(purpose).colorForDepth(depth)
}
static func positiveAction() -> StyleColor {
return StyleColor(
normal: DepthColor(foreground: 0x45A332, background: 0xFFFFFF),
highlighted: DepthColor(foreground: 0x45A332, background: 0xFFFFFF),
disabled: DepthColor(foreground: 0x888888, background: 0xFFFFFF)
)
}
static func deleteAction() -> StyleColor {
return StyleColor(
normal: DepthColor(foreground: 0xF31717, background: 0xFFFFFF),
highlighted: DepthColor(foreground: 0xD91414, background: 0xFFFFFF),
disabled: DepthColor(foreground: 0x888888, background: 0xFFFFFF)
)
}
static func neutralAction() -> StyleColor {
return StyleColor(
normal: DepthColor(foreground: 0x666666, background: 0xFFFFFF),
highlighted: DepthColor(foreground: 0x555555, background: 0xFFFFFF),
disabled: DepthColor(foreground: 0x888888, background: 0xFFFFFF)
)
}
}var color = StyleColor.positiveAction().color()
color = StyleColor.positiveAction().color(.Highlighted)
color = StyleColor.positiveAction().color(.Highlighted, .Background)class StyleButton: UIButton {
var style: StyleColor = StyleColor() {
didSet {
self._configureView()
}
}
init(coder aDecoder: NSCoder!, style : StyleColor) {
self.style = style
super.init(coder: aDecoder)
self._configureView()
}
init() {
super.init(frame: CGRect())
self._configureView()
}
func _configureView() {
self.showsTouchWhenHighlighted = false
self.adjustsImageWhenHighlighted = false
self.adjustsImageWhenDisabled = false
self.reversesTitleShadowWhenHighlighted = false
self.setTitleColor(style.color(.Normal, .Background), forState: .Normal)
self.setTitleColor(style.color(.Disabled, .Background), forState: .Disabled)
self.setTitleColor(style.color(.Highlighted, .Background), forState: .Highlighted)
self.setBackgroundImage(UIImage(color: style.color()), forState: .Normal)
self.setBackgroundImage(UIImage(color: style.color(.Disabled)), forState: .Disabled)
self.setBackgroundImage(UIImage(color: style.color(.Highlighted)), forState: .Highlighted)
}
}Context
StackExchange Code Review Q#54642, answer score: 4
Revisions (0)
No revisions yet.