From a570352abc18f355c8baff1419943208c6490a47 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 27 Feb 2019 08:54:49 +0000 Subject: [PATCH] Rename Image to ImageView, add Image props (#55) This allows adding props for components that accept images, for example `Switch` is now able to implement `onImage` and `offImage` props. * Add separate (empty for now) Image prop * Implement Image, add Switch {on,off}Image props --- Example/Tokamak/Components/Image.swift | 4 +- Sources/Tokamak/Components/Host/Image.swift | 68 ------------------- .../Tokamak/Components/Host/ImageView.swift | 23 +++++++ Sources/Tokamak/Components/Host/Switch.swift | 6 ++ Sources/Tokamak/Components/Props/Image.swift | 57 ++++++++++++++++ .../Host/{Image.swift => ImageView.swift} | 21 ++---- .../TokamakUIKit/Components/Host/Label.swift | 2 +- .../TokamakUIKit/Components/Host/Switch.swift | 5 +- .../TokamakUIKit/Components/Props/Image.swift | 28 ++++++++ Tokamak.xcodeproj/project.pbxproj | 26 ++++--- 10 files changed, 142 insertions(+), 98 deletions(-) delete mode 100644 Sources/Tokamak/Components/Host/Image.swift create mode 100644 Sources/Tokamak/Components/Host/ImageView.swift create mode 100644 Sources/Tokamak/Components/Props/Image.swift rename Sources/TokamakUIKit/Components/Host/{Image.swift => ImageView.swift} (55%) create mode 100644 Sources/TokamakUIKit/Components/Props/Image.swift diff --git a/Example/Tokamak/Components/Image.swift b/Example/Tokamak/Components/Image.swift index 098f7220..1948aac8 100644 --- a/Example/Tokamak/Components/Image.swift +++ b/Example/Tokamak/Components/Image.swift @@ -18,9 +18,9 @@ struct ImageExample: PureLeafComponent { axis: .vertical, distribution: .fillEqually ), [ - Image.node(.init( + ImageView.node(.init( Style(contentMode: .scaleAspectFit), - name: "tokamak" + image: Image(name: "tokamak") )), ]) } diff --git a/Sources/Tokamak/Components/Host/Image.swift b/Sources/Tokamak/Components/Host/Image.swift deleted file mode 100644 index d6a7f0e9..00000000 --- a/Sources/Tokamak/Components/Host/Image.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// Image.swift -// Tokamak -// -// Created by Matvii Hodovaniuk on 2/17/19. -// - -import Foundation - -public struct Image: HostComponent { - public typealias Children = [AnyNode] - - public struct Props: Equatable, StyleProps { - public enum RenderingMode { - case automatic - case alwaysOriginal - case alwaysTemplate - } - - public enum Source: Equatable { - case name(String) - case data(Data) - } - - // when changed initializes new image with `UIImage(named:)` - // or `UIImage(data:)` - public let source: Source - - // when changed creates new image with `withRenderingMode` - public let renderingMode: RenderingMode - - // when changed initializes new image with given scale - public let scale: Double - - // mirrors `flipsForRightToLeftLayoutDirection` - public let flipsForRTL: Bool - - public let style: Style? - - public init( - _ style: Style? = nil, - flipsForRTL: Bool = false, - name: String, - renderingMode: RenderingMode = .automatic, - scale: Double = 1.0 - ) { - source = .name(name) - self.renderingMode = renderingMode - self.scale = scale - self.flipsForRTL = flipsForRTL - self.style = style - } - - public init( - _ style: Style? = nil, - data: Data, - flipsForRTL: Bool = false, - renderingMode: RenderingMode = .automatic, - scale: Double = 1.0 - ) { - source = .data(data) - self.renderingMode = renderingMode - self.scale = scale - self.flipsForRTL = flipsForRTL - self.style = style - } - } -} diff --git a/Sources/Tokamak/Components/Host/ImageView.swift b/Sources/Tokamak/Components/Host/ImageView.swift new file mode 100644 index 00000000..8c527bc5 --- /dev/null +++ b/Sources/Tokamak/Components/Host/ImageView.swift @@ -0,0 +1,23 @@ +// +// ImageView.swift +// Tokamak +// +// Created by Matvii Hodovaniuk on 2/17/19. +// + +public struct ImageView: HostComponent { + public typealias Children = [AnyNode] + + public struct Props: Equatable, StyleProps { + public let image: Image + public let style: Style? + + public init( + _ style: Style? = nil, + image: Image + ) { + self.image = image + self.style = style + } + } +} diff --git a/Sources/Tokamak/Components/Host/Switch.swift b/Sources/Tokamak/Components/Host/Switch.swift index b75a2564..5a1e1e1d 100644 --- a/Sources/Tokamak/Components/Host/Switch.swift +++ b/Sources/Tokamak/Components/Host/Switch.swift @@ -14,12 +14,16 @@ public struct Switch: HostComponent { public let valueHandler: Handler? public let isEnabled: Bool public let isAnimated: Bool + public let onImage: Image? + public let offImage: Image? public init( _ style: Style? = nil, handlers: EventHandlers = [:], isAnimated: Bool = true, isEnabled: Bool = true, + offImage: Image? = nil, + onImage: Image? = nil, value: Bool, valueHandler: Handler? = nil ) { @@ -29,6 +33,8 @@ public struct Switch: HostComponent { self.valueHandler = valueHandler self.isEnabled = isEnabled self.isAnimated = isAnimated + self.offImage = offImage + self.onImage = onImage } } diff --git a/Sources/Tokamak/Components/Props/Image.swift b/Sources/Tokamak/Components/Props/Image.swift new file mode 100644 index 00000000..0013f3b7 --- /dev/null +++ b/Sources/Tokamak/Components/Props/Image.swift @@ -0,0 +1,57 @@ +// +// Image.swift +// Tokamak +// +// Created by Max Desiatov on 26/02/2019. +// + +import Foundation + +public struct Image: Equatable { + public enum RenderingMode { + case automatic + case alwaysOriginal + case alwaysTemplate + } + + public enum Source: Equatable { + case name(String) + case data(Data) + } + + /// when changed initializes new image with given data or name + public let source: Source + + /// when changed creates new image with `withRenderingMode` + public let renderingMode: RenderingMode + + /// equivalent to `flipsForRightToLeftLayoutDirection` in UIKit + public let flipsForRTL: Bool + + // when changed initializes new image with given scale + public let scale: Double + + public init( + flipsForRTL: Bool = false, + name: String, + renderingMode: RenderingMode = .automatic, + scale: Double = 1.0 + ) { + source = .name(name) + self.renderingMode = renderingMode + self.scale = scale + self.flipsForRTL = flipsForRTL + } + + public init( + data: Data, + flipsForRTL: Bool = false, + renderingMode: RenderingMode = .automatic, + scale: Double = 1.0 + ) { + source = .data(data) + self.renderingMode = renderingMode + self.scale = scale + self.flipsForRTL = flipsForRTL + } +} diff --git a/Sources/TokamakUIKit/Components/Host/Image.swift b/Sources/TokamakUIKit/Components/Host/ImageView.swift similarity index 55% rename from Sources/TokamakUIKit/Components/Host/Image.swift rename to Sources/TokamakUIKit/Components/Host/ImageView.swift index e6b3c0fe..e307069e 100644 --- a/Sources/TokamakUIKit/Components/Host/Image.swift +++ b/Sources/TokamakUIKit/Components/Host/ImageView.swift @@ -15,7 +15,7 @@ final class TokamakImage: UIImageView, Default { } extension UIImage.RenderingMode { - public init(_ rawValue: Image.Props.RenderingMode) { + public init(_ rawValue: Image.RenderingMode) { switch rawValue { case .automatic: self = .automatic @@ -27,27 +27,14 @@ extension UIImage.RenderingMode { } } -extension Image: UIViewComponent { +extension ImageView: UIViewComponent { public typealias RefTarget = UIImageView static func update( view box: ViewBox, - _ props: Image.Props, + _ props: ImageView.Props, _ children: [AnyNode] ) { - var image: UIImage? - - switch props.source { - case let .name(name): - image = UIImage(named: name) - case let .data(data): - image = UIImage(data: data, scale: CGFloat(props.scale)) - } - - if props.flipsForRTL { - image = image?.imageFlippedForRightToLeftLayoutDirection() - } - - box.view.image = image?.withRenderingMode(.init(props.renderingMode)) + box.view.image = UIImage.from(image: props.image) } } diff --git a/Sources/TokamakUIKit/Components/Host/Label.swift b/Sources/TokamakUIKit/Components/Host/Label.swift index 3e51905e..3bddcc1e 100644 --- a/Sources/TokamakUIKit/Components/Host/Label.swift +++ b/Sources/TokamakUIKit/Components/Host/Label.swift @@ -41,7 +41,7 @@ extension Label: UIViewComponent { view.textAlignment = NSTextAlignment(props.alignment) view.numberOfLines = props.numberOfLines view.lineBreakMode = NSLineBreakMode(props.lineBreakMode) - props.textColor.flatMap { view.textColor = UIColor($0) } + view.textColor = props.textColor.flatMap { UIColor($0) } view.text = children } } diff --git a/Sources/TokamakUIKit/Components/Host/Switch.swift b/Sources/TokamakUIKit/Components/Host/Switch.swift index be450c0c..7e281f99 100644 --- a/Sources/TokamakUIKit/Components/Host/Switch.swift +++ b/Sources/TokamakUIKit/Components/Host/Switch.swift @@ -32,6 +32,9 @@ extension Switch: UIValueComponent { static func update(valueBox: ValueControlBox, _ props: Switch.Props, _ children: Null) { - valueBox.view.isAnimated = props.isAnimated + let view = valueBox.view + view.isAnimated = props.isAnimated + view.onImage = props.onImage.flatMap { UIImage.from(image: $0) } + view.offImage = props.onImage.flatMap { UIImage.from(image: $0) } } } diff --git a/Sources/TokamakUIKit/Components/Props/Image.swift b/Sources/TokamakUIKit/Components/Props/Image.swift new file mode 100644 index 00000000..f95cbfcd --- /dev/null +++ b/Sources/TokamakUIKit/Components/Props/Image.swift @@ -0,0 +1,28 @@ +// +// Image.swift +// Tokamak +// +// Created by Max Desiatov on 26/02/2019. +// + +import Tokamak +import UIKit + +extension UIImage { + static func from(image: Image) -> UIImage? { + var result: UIImage? + + switch image.source { + case let .name(name): + result = UIImage(named: name) + case let .data(data): + result = UIImage(data: data, scale: CGFloat(image.scale)) + } + + if image.flipsForRTL { + result = result?.imageFlippedForRightToLeftLayoutDirection() + } + + return result + } +} diff --git a/Tokamak.xcodeproj/project.pbxproj b/Tokamak.xcodeproj/project.pbxproj index 66d317f5..7fb055c3 100644 --- a/Tokamak.xcodeproj/project.pbxproj +++ b/Tokamak.xcodeproj/project.pbxproj @@ -25,8 +25,10 @@ A6AB60B5221C70340063F88A /* ContentMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6AB60B4221C70340063F88A /* ContentMode.swift */; }; A6D188EC221F1CA300C3947C /* Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D188EB221F1CA300C3947C /* Accessibility.swift */; }; A6D188EE2220221B00C3947C /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D188ED2220221B00C3947C /* TextField.swift */; }; - D1B4F8DB221AFB0E00C53C42 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4F8D9221AFB0800C53C42 /* Image.swift */; }; - D1B4F8DD221AFB2B00C53C42 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4F8DC221AFB2B00C53C42 /* Image.swift */; }; + D1B4F8DB221AFB0E00C53C42 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4F8D9221AFB0800C53C42 /* ImageView.swift */; }; + D1B4F8DD221AFB2B00C53C42 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4F8DC221AFB2B00C53C42 /* ImageView.swift */; }; + D1CFC81D222546F500B03222 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1CFC81C222546F500B03222 /* Image.swift */; }; + D1CFC8202225622D00B03222 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1CFC81E222561AD00B03222 /* Image.swift */; }; OBJ_153 /* AnyEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_66 /* AnyEquatable.swift */; }; OBJ_154 /* AnyNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_67 /* AnyNode.swift */; }; OBJ_155 /* Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_68 /* Components.swift */; }; @@ -190,8 +192,10 @@ A6D188EB221F1CA300C3947C /* Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessibility.swift; sourceTree = ""; }; A6D188ED2220221B00C3947C /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; }; A6D188EF2220222F00C3947C /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; }; - D1B4F8D9221AFB0800C53C42 /* Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; - D1B4F8DC221AFB2B00C53C42 /* Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; + D1B4F8D9221AFB0800C53C42 /* ImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = ""; }; + D1B4F8DC221AFB2B00C53C42 /* ImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = ""; }; + D1CFC81C222546F500B03222 /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; + D1CFC81E222561AD00B03222 /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; OBJ_10 /* ContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerViewController.swift; sourceTree = ""; }; OBJ_100 /* FirstBaseline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstBaseline.swift; sourceTree = ""; }; OBJ_101 /* Height.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Height.swift; sourceTree = ""; }; @@ -418,9 +422,9 @@ OBJ_17 /* Host */ = { isa = PBXGroup; children = ( - D1B4F8D9221AFB0800C53C42 /* Image.swift */, OBJ_18 /* Button.swift */, OBJ_19 /* DatePicker.swift */, + D1B4F8D9221AFB0800C53C42 /* ImageView.swift */, OBJ_20 /* Label.swift */, OBJ_21 /* ListView.swift */, OBJ_22 /* ModalPresenter.swift */, @@ -443,6 +447,7 @@ OBJ_32 /* Color.swift */, OBJ_33 /* Constraint */, OBJ_52 /* Event.swift */, + D1CFC81E222561AD00B03222 /* Image.swift */, OBJ_53 /* LineBreakMode.swift */, OBJ_54 /* Rectangle.swift */, A6AB60B4221C70340063F88A /* ContentMode.swift */, @@ -555,11 +560,11 @@ OBJ_70 /* Host */ = { isa = PBXGroup; children = ( - D1B4F8DC221AFB2B00C53C42 /* Image.swift */, OBJ_71 /* Alert.swift */, OBJ_72 /* Animated.swift */, OBJ_73 /* Button.swift */, OBJ_74 /* DatePicker.swift */, + D1B4F8DC221AFB2B00C53C42 /* ImageView.swift */, OBJ_75 /* Label.swift */, OBJ_76 /* ListView.swift */, OBJ_77 /* NavigationController.swift */, @@ -568,8 +573,8 @@ OBJ_80 /* StackView.swift */, OBJ_81 /* Stepper.swift */, OBJ_82 /* Switch.swift */, - OBJ_83 /* View.swift */, A6D188ED2220221B00C3947C /* TextField.swift */, + OBJ_83 /* View.swift */, ); path = Host; sourceTree = ""; @@ -619,6 +624,7 @@ OBJ_93 /* Constraint */, OBJ_110 /* Event.swift */, OBJ_111 /* Insets.swift */, + D1CFC81C222546F500B03222 /* Image.swift */, OBJ_112 /* LineBreakMode.swift */, OBJ_113 /* Rectangle.swift */, OBJ_114 /* Second.swift */, @@ -781,6 +787,7 @@ OBJ_159 /* DatePicker.swift in Sources */, OBJ_160 /* Label.swift in Sources */, OBJ_161 /* ListView.swift in Sources */, + D1CFC81D222546F500B03222 /* Image.swift in Sources */, OBJ_162 /* NavigationController.swift in Sources */, OBJ_163 /* SegmentedControl.swift in Sources */, OBJ_164 /* Slider.swift in Sources */, @@ -798,7 +805,7 @@ OBJ_176 /* Bottom.swift in Sources */, A6D188EE2220221B00C3947C /* TextField.swift in Sources */, OBJ_177 /* Center.swift in Sources */, - D1B4F8DD221AFB2B00C53C42 /* Image.swift in Sources */, + D1B4F8DD221AFB2B00C53C42 /* ImageView.swift in Sources */, A6D188EC221F1CA300C3947C /* Accessibility.swift in Sources */, OBJ_178 /* CenterX.swift in Sources */, OBJ_179 /* CenterY.swift in Sources */, @@ -896,8 +903,9 @@ OBJ_276 /* CenterX.swift in Sources */, OBJ_277 /* CenterY.swift in Sources */, OBJ_278 /* Constrainable.swift in Sources */, - D1B4F8DB221AFB0E00C53C42 /* Image.swift in Sources */, + D1B4F8DB221AFB0E00C53C42 /* ImageView.swift in Sources */, OBJ_279 /* Constraint.swift in Sources */, + D1CFC8202225622D00B03222 /* Image.swift in Sources */, OBJ_280 /* FirstBaseline.swift in Sources */, OBJ_281 /* Height.swift in Sources */, OBJ_282 /* LastBaseline.swift in Sources */,