Skip to content

iOS improvements #94

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
41 changes: 33 additions & 8 deletions Few-iOS/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@

import UIKit

extension UIControlState: Hashable {
static let all: [UIControlState] = [.Normal, .Selected, .Disabled, .Highlighted]
public var hashValue: Int {
return Int(rawValue)
}
}

public class Button: Element {
public var attributedTitles: [UIControlState: NSAttributedString]
public var images: [UIControlState: UIImage]
Expand All @@ -23,6 +16,8 @@ public class Button: Element {
public var selected: Bool
public var highlighted: Bool

private static let layoutButton = UIButton()

private var trampoline = TargetActionTrampoline()

public convenience init(attributedTitle: NSAttributedString = NSAttributedString(), image: UIImage? = nil, action: (() -> Void) = { }) {
Expand All @@ -43,7 +38,37 @@ public class Button: Element {
self.enabled = enabled
self.highlighted = highlighted
trampoline.action = action
super.init(frame: CGRect(x: 0, y: 0, width: 50, height: 23))
super.init()
}

public var controlState: UIControlState {
return UIControlState(enabled: enabled, selected: selected, highlighted: highlighted)
}

public override func assembleLayoutNode() -> Node {
let childNodes = children.map { $0.assembleLayoutNode() }

return Node(size: frame.size, children: childNodes, direction: direction, margin: marginWithPlatformSpecificAdjustments, padding: paddingWithPlatformSpecificAdjustments, wrap: wrap, justification: justification, selfAlignment: selfAlignment, childAlignment: childAlignment, flex: flex) { w in
let controlState = self.controlState

let layoutButton = Button.layoutButton
layoutButton.enabled = self.enabled
layoutButton.highlighted = self.highlighted
layoutButton.selected = self.selected

let attributedTitle = self.attributedTitles[controlState] ?? self.attributedTitles[.Normal]
if layoutButton.attributedTitleForState(controlState) != attributedTitle {
layoutButton.setAttributedTitle(attributedTitle, forState: controlState)
}

let image = self.images[controlState] ?? self.images[.Normal]
if image != layoutButton.imageForState(controlState) {
layoutButton.setImage(image, forState: controlState)
}

let fittingSize = CGSize(width: w.isNaN ? ABigDimension : w, height: ABigDimension)
return layoutButton.sizeThatFits(fittingSize)
}
}

// MARK: Element
Expand Down
2 changes: 1 addition & 1 deletion Few-iOS/Label.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import UIKit

private let DefaultLabelFont = UIFont.systemFontOfSize(UIFont.systemFontSize())

private let ABigDimension: CGFloat = 10000
internal let ABigDimension: CGFloat = 10000

private let sizingLabel = UILabel()

Expand Down
74 changes: 74 additions & 0 deletions Few-iOS/Switch.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

import UIKit

public class Switch: Element {
static let intrinsicSize = UISwitch().intrinsicContentSize()

public init(on: Bool, enabled: Bool = true, animatesOnSetting: Bool = true, onTintColor: UIColor? = nil, tintColor: UIColor? = nil, thumbTintColor: UIColor? = nil, action: (Bool -> Void)? = nil) {
let initialFrame = CGRect(origin: .zeroPoint, size: Switch.intrinsicSize)
self.on = on
self.onTintColor = onTintColor
self.thumbTintColor = thumbTintColor
self.tintColor = tintColor
self.animatesOnSetting = animatesOnSetting
self.enabled = enabled
self.action = action
super.init(frame: initialFrame)
if let action = action {
trampoline.action = { aSwitch in action(aSwitch.on) }
}
}

private var trampoline = TargetActionTrampolineWithSender<UISwitch>()
public var on: Bool
public var enabled: Bool
public var action: (Bool -> Void)?
public var animatesOnSetting: Bool

public var onTintColor: UIColor?
public var tintColor: UIColor?
public var thumbTintColor: UIColor?

public override func applyDiff(old: Element, realizedSelf: RealizedElement?) {
super.applyDiff(old, realizedSelf: realizedSelf)

if let view = realizedSelf?.view as? UISwitch {
if let oldSwitch = old as? Switch {
let newTrampoline = oldSwitch.trampoline
newTrampoline.action = trampoline.action // Make sure the newest action is used
trampoline = newTrampoline
}

if enabled != view.enabled {
view.enabled = enabled
}

if on != view.on {
view.setOn(on, animated: animatesOnSetting)
}

if view.onTintColor != onTintColor {
view.onTintColor = onTintColor
}

if view.tintColor != tintColor {
view.tintColor = tintColor
}

if view.thumbTintColor != thumbTintColor {
view.thumbTintColor = thumbTintColor
}
}
}

public override func createView() -> ViewType? {
let view = UISwitch()
view.on = on
view.enabled = enabled
view.onTintColor = onTintColor
view.tintColor = tintColor
view.thumbTintColor = thumbTintColor
view.addTarget(trampoline.target, action: trampoline.selector, forControlEvents: .ValueChanged)
return view
}
}
17 changes: 15 additions & 2 deletions Few-iOS/TableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,19 @@ public class TableView: Element {
private let sectionFooters: [Element?]
private let header: Element?
private let footer: Element?
private let contentInset: UIEdgeInsets
private let scrollIndicatorInsets: UIEdgeInsets

public init(_ elements: [[Element]], sectionHeaders: [Element?] = [], sectionFooters: [Element?] = [], header: Element? = nil, footer: Element? = nil, selectedRow: NSIndexPath? = nil, selectionChanged: (NSIndexPath -> ())? = nil) {
public init(_ elements: [[Element]], sectionHeaders: [Element?] = [], sectionFooters: [Element?] = [], header: Element? = nil, footer: Element? = nil, selectedRow: NSIndexPath? = nil, contentInset: UIEdgeInsets = .zeroInsets, scrollIndicatorInsets: UIEdgeInsets = .zeroInsets, selectionChanged: (NSIndexPath -> ())? = nil) {
self.elements = elements
self.selectionChanged = selectionChanged
self.selectedRow = selectedRow
self.sectionHeaders = sectionHeaders
self.sectionFooters = sectionFooters
self.header = header
self.footer = footer
self.contentInset = contentInset
self.scrollIndicatorInsets = scrollIndicatorInsets
}

// MARK: -
Expand Down Expand Up @@ -295,7 +299,14 @@ public class TableView: Element {
} else if tableView.tableFooterView == handler.footerView {
tableView.tableFooterView = nil
}


if contentInset != tableView.contentInset {
tableView.contentInset = contentInset
}

if scrollIndicatorInsets != tableView.scrollIndicatorInsets {
tableView.scrollIndicatorInsets = scrollIndicatorInsets
}
}

}
Expand All @@ -307,6 +318,8 @@ public class TableView: Element {
tableView.handler?.selectionChanged = selectionChanged
tableView.alpha = alpha
tableView.hidden = hidden
tableView.contentInset = contentInset
tableView.scrollIndicatorInsets = scrollIndicatorInsets
if let header = header {
handler.headerView.updateWithElement(header)
let layout = header.assembleLayoutNode().layout(maxWidth: tableView.frame.width)
Expand Down
35 changes: 35 additions & 0 deletions Few-iOS/UIControlStateExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

import UIKit

extension UIControlState: Hashable {
static let all: Set<UIControlState> = {
var states = Set<UIControlState>()
for enabled in [false, true] {
for selected in [false, true] {
for highlighted in [false, true] {
let state = UIControlState(enabled: enabled, selected: selected, highlighted: highlighted)
states.insert(state)
}
}
}
return states
}()

public var hashValue: Int {
return Int(rawValue)
}

init(enabled: Bool, selected: Bool, highlighted: Bool) {
var result = UIControlState.Normal
if !enabled {
result |= .Disabled
}
if selected {
result |= .Selected
}
if highlighted {
result |= .Highlighted
}
self = result
}
}
10 changes: 10 additions & 0 deletions Few-iOS/UIGeometryExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

import UIKit

extension UIEdgeInsets: Equatable {
static let zeroInsets = UIEdgeInsetsZero
}

public func ==(inset0: UIEdgeInsets, inset1: UIEdgeInsets) -> Bool {
return UIEdgeInsetsEqualToEdgeInsets(inset0, inset1)
}
26 changes: 26 additions & 0 deletions Few.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
CC2C508B1AFECB0600019685 /* SwiftBox.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CC2C507D1AFEC95900019685 /* SwiftBox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CC2C508C1AFECB0B00019685 /* SwiftBox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC2C507A1AFEC93D00019685 /* SwiftBox.framework */; };
CC2C508D1AFECB0B00019685 /* SwiftBox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CC2C507A1AFEC93D00019685 /* SwiftBox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CC8A32361B54AEB700626C4D /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC8A32351B54AEB700626C4D /* Switch.swift */; };
CC95B32B1B54BD8B00F7D83C /* AttributedStringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC95B32A1B54BD8B00F7D83C /* AttributedStringExtensions.swift */; };
CC95B32C1B54BD8B00F7D83C /* AttributedStringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC95B32A1B54BD8B00F7D83C /* AttributedStringExtensions.swift */; };
CC95B32E1B54BE3C00F7D83C /* UIControlStateExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC95B32D1B54BE3C00F7D83C /* UIControlStateExtensions.swift */; };
CC95B3311B54C1D100F7D83C /* UIGeometryExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC95B3301B54C1D100F7D83C /* UIGeometryExtensions.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -190,6 +195,10 @@
CC2C507D1AFEC95900019685 /* SwiftBox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftBox.framework; path = Carthage/Build/Mac/SwiftBox.framework; sourceTree = "<group>"; };
CC2C50801AFEC99700019685 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/Mac/Quick.framework; sourceTree = SOURCE_ROOT; };
CC2C50841AFECA8100019685 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/Mac/Nimble.framework; sourceTree = SOURCE_ROOT; };
CC8A32351B54AEB700626C4D /* Switch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Switch.swift; sourceTree = "<group>"; };
CC95B32A1B54BD8B00F7D83C /* AttributedStringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributedStringExtensions.swift; sourceTree = "<group>"; };
CC95B32D1B54BE3C00F7D83C /* UIControlStateExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlStateExtensions.swift; sourceTree = "<group>"; };
CC95B3301B54C1D100F7D83C /* UIGeometryExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGeometryExtensions.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -243,9 +252,11 @@
888E59AE1A4684850062A36B /* Few-iOS */ = {
isa = PBXGroup;
children = (
CC95B32F1B54BE4400F7D83C /* Extensions */,
888E59CC1A4684AF0062A36B /* iOS.swift */,
A2FA89121AB33C1F00561FBF /* View.swift */,
A2FA89141AB33E0800561FBF /* Button.swift */,
CC8A32351B54AEB700626C4D /* Switch.swift */,
A2FA89161AB33EDA00561FBF /* Label.swift */,
A2B9C7471AB387F7002648DA /* TableView.swift */,
A2B9C74F1AB390B3002648DA /* Image.swift */,
Expand Down Expand Up @@ -319,6 +330,7 @@
8897689419AED8AF00DD079B /* Util.swift */,
8897689319AED8AF00DD079B /* TargetActionTrampoline.swift */,
888E59DA1A4687E10062A36B /* QuickLook.swift */,
CC95B32A1B54BD8B00F7D83C /* AttributedStringExtensions.swift */,
);
path = FewCore;
sourceTree = "<group>";
Expand Down Expand Up @@ -412,6 +424,15 @@
name = "Frameworks-Mac";
sourceTree = "<group>";
};
CC95B32F1B54BE4400F7D83C /* Extensions */ = {
isa = PBXGroup;
children = (
CC95B32D1B54BE3C00F7D83C /* UIControlStateExtensions.swift */,
CC95B3301B54C1D100F7D83C /* UIGeometryExtensions.swift */,
);
name = Extensions;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand Down Expand Up @@ -624,14 +645,18 @@
buildActionMask = 2147483647;
files = (
A2B9C7541AB43D0A002648DA /* Input.swift in Sources */,
CC95B32E1B54BE3C00F7D83C /* UIControlStateExtensions.swift in Sources */,
888E59D81A4687330062A36B /* Util.swift in Sources */,
888E59CF1A4687330062A36B /* Element.swift in Sources */,
CC95B32C1B54BD8B00F7D83C /* AttributedStringExtensions.swift in Sources */,
A2FA89171AB33EDA00561FBF /* Label.swift in Sources */,
A2FA89131AB33C1F00561FBF /* View.swift in Sources */,
CC95B3311B54C1D100F7D83C /* UIGeometryExtensions.swift in Sources */,
888E59D01A4687330062A36B /* Component.swift in Sources */,
888E59D91A4687330062A36B /* TargetActionTrampoline.swift in Sources */,
888E59CD1A4684AF0062A36B /* iOS.swift in Sources */,
A2B9C7501AB390B3002648DA /* Image.swift in Sources */,
CC8A32361B54AEB700626C4D /* Switch.swift in Sources */,
888E59D41A4687330062A36B /* Empty.swift in Sources */,
A2B9C7481AB387F7002648DA /* TableView.swift in Sources */,
889FE1BE1A4E11F000D53F3E /* QuickLook.swift in Sources */,
Expand All @@ -648,6 +673,7 @@
88995D6D1A855D2000526313 /* View.swift in Sources */,
883CECA81A4532A600B8A510 /* Password.swift in Sources */,
889768A019AED8AF00DD079B /* Util.swift in Sources */,
CC95B32B1B54BD8B00F7D83C /* AttributedStringExtensions.swift in Sources */,
8897689619AED8AF00DD079B /* Button.swift in Sources */,
8897689919AED8AF00DD079B /* Element.swift in Sources */,
8897689C19AED8AF00DD079B /* Label.swift in Sources */,
Expand Down
8 changes: 8 additions & 0 deletions FewCore/AttributedStringExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

import Foundation

extension NSAttributedString: Equatable { }

public func ==(str0: NSAttributedString, str1: NSAttributedString) -> Bool {
return str0.isEqualToAttributedString(str1)
}
20 changes: 18 additions & 2 deletions FewDemo-iOS/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,28 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

window = UIWindow(frame: UIScreen.mainScreen().bounds)
let vc = UIViewController()
vc.title = "Few Demo"
vc.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .Plain, target: self, action: "nextTapped")
let nav = UINavigationController(rootViewController: vc)
vc.view.backgroundColor = UIColor.whiteColor()
appComponent.addToView(vc.view)
window?.rootViewController = vc
window?.rootViewController = nav
window?.makeKeyAndVisible()

return true
}

@objc func nextTapped() {
appComponent.updateState { (var state) in
switch state.activeComponent {
case .TableView:
state.activeComponent = .Counter
case .Counter:
state.activeComponent = .Input
case .Input:
state.activeComponent = .TableView
}
return state
}
}
}

Loading