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

feat: Replace the foregroundStore Set<UIView> with an array [UIView] #2

Open
wants to merge 1 commit 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
12 changes: 8 additions & 4 deletions Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
523646911C6F88CF00392180 /* StatefulViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D6451661A64089800108EA3 /* StatefulViewController.swift */; };
523646921C6F88CF00392180 /* StatefulViewControllerImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DC230DC1BB5BA810083B95A /* StatefulViewControllerImplementation.swift */; };
523646931C6F88CF00392180 /* ViewStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D6451671A64089800108EA3 /* ViewStateMachine.swift */; };
D32EF03F2358D27F0001D9B5 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D32EF03E2358D27F0001D9B5 /* GradientView.swift */; };
D377D48A21D5018800C93544 /* ForegroundViewStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D377D48921D5018800C93544 /* ForegroundViewStoreViewController.swift */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -98,6 +99,7 @@
4DE62B0B19B65AF00021630A /* ErrorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
4DE62B0C19B65AF00021630A /* LoadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = "<group>"; };
523646881C6F87B000392180 /* StatefulViewController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = StatefulViewController.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D32EF03E2358D27F0001D9B5 /* GradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = "<group>"; };
D377D48921D5018800C93544 /* ForegroundViewStoreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForegroundViewStoreViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -202,6 +204,7 @@
4D0EA4711ECAF83E00139926 /* TableViewController.swift */,
4D0EA4731ECB052000139926 /* CollectionViewController.swift */,
D377D48921D5018800C93544 /* ForegroundViewStoreViewController.swift */,
D32EF03E2358D27F0001D9B5 /* GradientView.swift */,
4DE62B0819B65AF00021630A /* PlaceholderViews */,
4DE62AE519B658610021630A /* Main.storyboard */,
4D137EB619C1BE5700AC1050 /* LaunchScreen.xib */,
Expand Down Expand Up @@ -438,6 +441,7 @@
files = (
4DE62AE419B658610021630A /* ViewController.swift in Sources */,
4D0EA4721ECAF83E00139926 /* TableViewController.swift in Sources */,
D32EF03F2358D27F0001D9B5 /* GradientView.swift in Sources */,
4DE62B0D19B65AF00021630A /* BasicPlaceholderView.swift in Sources */,
D377D48A21D5018800C93544 /* ForegroundViewStoreViewController.swift in Sources */,
4DE62AE219B658610021630A /* AppDelegate.swift in Sources */,
Expand Down Expand Up @@ -515,7 +519,7 @@
);
INFOPLIST_FILE = StatefulViewController/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = at.allaboutapps.StatefulViewController;
PRODUCT_NAME = StatefulViewController;
Expand All @@ -541,7 +545,7 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = StatefulViewController/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = at.allaboutapps.StatefulViewController;
PRODUCT_NAME = StatefulViewController;
Expand Down Expand Up @@ -738,7 +742,7 @@
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
TVOS_DEPLOYMENT_TARGET = 10.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
Expand All @@ -764,7 +768,7 @@
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
TVOS_DEPLOYMENT_TARGET = 10.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
Expand Down
143 changes: 93 additions & 50 deletions Example/Base.lproj/Main.storyboard

Large diffs are not rendered by default.

29 changes: 22 additions & 7 deletions Example/ForegroundViewStoreViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ class ForegroundViewStoreViewController: UIViewController, StatefulViewControlle

@IBOutlet weak var addButton: UIButton!

@IBOutlet weak var headerView: UIView!

@IBOutlet weak var headerLabel: UILabel!

@IBOutlet weak var overlayView: UIView!

@IBOutlet weak var stateFullContainer: UIView!

// MARK: - Properties

private let refreshControl = UIRefreshControl()
Expand All @@ -34,16 +42,22 @@ class ForegroundViewStoreViewController: UIViewController, StatefulViewControlle
refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
tableView.addSubview(refreshControl)

let topInset = (navigationController?.navigationBar.frame.origin.y ?? 0)
+ headerView.frame.origin.y
+ headerView.frame.size.height
let insets = UIEdgeInsets(top: topInset, left: 0, bottom: 0, right: 0)

// Setup placeholder views
loadingView = LoadingView(frame: view.frame)
emptyView = EmptyView(frame: view.frame)
loadingView = LoadingView(frame: view.frame).prepare(insets: insets)
emptyView = EmptyView(frame: view.frame).prepare(insets: insets)
let failureView = ErrorView(frame: view.frame)
failureView.tapGestureRecognizer.addTarget(self, action: #selector(refresh))
errorView = failureView
errorView = failureView.prepare(insets: insets)

foregroundViewStore = [
.empty: [addButton],
.error: [addButton]
.empty: [overlayView, headerLabel, addButton],
.error: [overlayView, headerLabel, addButton],
.loading: [addButton]
]
}

Expand Down Expand Up @@ -92,11 +106,12 @@ class ForegroundViewStoreViewController: UIViewController, StatefulViewControlle
}

@IBAction func onDeleteButton(_ sender: Any) {
foregroundViewStore?[.empty]?.remove(deleteButton)
guard let indexOfDeleteButton = foregroundViewStore?[.empty]?.firstIndex(of: deleteButton) else { return }
foregroundViewStore?[.empty]?.remove(at: indexOfDeleteButton)
}

@IBAction func onAddButton(_ sender: Any) {
foregroundViewStore?[.empty]?.insert(deleteButton)
foregroundViewStore?[.empty]?.append(deleteButton)
}
}

Expand Down
46 changes: 46 additions & 0 deletions Example/GradientView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// GradientView.swift
// Example
//
// Created by Matthias Wagner on 17.10.19.
// Copyright © 2019 Alexander Schuch. All rights reserved.
//

import UIKit

@IBDesignable
class GradientView: UIView {
@IBInspectable var firstColor: UIColor = UIColor.clear {
didSet {
updateView()
}
}
@IBInspectable var secondColor: UIColor = UIColor.clear {
didSet {
updateView()
}
}
@IBInspectable var isHorizontal: Bool = true {
didSet {
updateView()
}
}
override class var layerClass: AnyClass {
get {
return CAGradientLayer.self
}
}
func updateView() {
let layer = self.layer as! CAGradientLayer
layer.colors = [firstColor, secondColor].map {$0.cgColor}
if (isHorizontal) {
layer.startPoint = CGPoint(x: 0, y: 0.5)
layer.endPoint = CGPoint (x: 1, y: 0.5)
} else {
layer.startPoint = CGPoint(x: 0.1, y: 0)
layer.endPoint = CGPoint (x: 0.9, y: 1)
}

layer.opacity = 0.4
}
}
2 changes: 2 additions & 0 deletions Example/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,7 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
</dict>
</plist>
13 changes: 12 additions & 1 deletion Example/PlaceholderViews/BasicPlaceholderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
//

import UIKit
import StatefulViewController

class BasicPlaceholderView: UIView {
class BasicPlaceholderView: UIView, StatefulPlaceholderView {

let centerView: UIView = UIView()

private var insets: UIEdgeInsets = .zero

override init(frame: CGRect) {
super.init(frame: frame)
Expand Down Expand Up @@ -40,4 +43,12 @@ class BasicPlaceholderView: UIView {
addConstraint(centerConstraint)
}

func prepare(insets: UIEdgeInsets = .zero) -> BasicPlaceholderView {
self.insets = insets
return self
}

func placeholderViewInsets() -> UIEdgeInsets {
return insets
}
}
7 changes: 1 addition & 6 deletions Example/PlaceholderViews/LoadingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import UIKit
import StatefulViewController

class LoadingView: BasicPlaceholderView, StatefulPlaceholderView {
class LoadingView: BasicPlaceholderView {

let label = UILabel()

Expand All @@ -34,9 +34,4 @@ class LoadingView: BasicPlaceholderView, StatefulPlaceholderView {
centerView.addConstraints(vConstraintsLabel)
centerView.addConstraints(vConstraintsActivity)
}

func placeholderViewInsets() -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}

}
2 changes: 1 addition & 1 deletion StatefulViewController/StatefulViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public protocol StatefulViewController: class, BackingViewProvider {
var emptyView: UIView? { get set }

/// This store includes the views, who should be in the front during the specific state (Key)
var foregroundViewStore: [StatefulViewControllerState: Set<UIView>]? { get set }
var foregroundViewStore: [StatefulViewControllerState: [UIView]]? { get set }

// MARK: Transitions

Expand Down
28 changes: 24 additions & 4 deletions StatefulViewController/StatefulViewControllerImplementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ extension StatefulViewController {
set { setPlaceholderView(newValue, forState: .empty) }
}

public var foregroundViewStore: [StatefulViewControllerState: Set<UIView>]? {
public var foregroundViewStore: [StatefulViewControllerState: [UIView]]? {
get { return getForegroundStore() }
set { setForegroundViewStore(newValue) }
}
Expand Down Expand Up @@ -123,12 +123,18 @@ extension StatefulViewController {
stateMachine[state.rawValue] = view
}

fileprivate func getForegroundStore() -> [StatefulViewControllerState: Set<UIView>]? {
fileprivate func getForegroundStore() -> [StatefulViewControllerState: [UIView]]? {
return stateMachine.foregroundViewStore
}

fileprivate func setForegroundViewStore(_ store: [StatefulViewControllerState: Set<UIView>]?) {
stateMachine.foregroundViewStore = store
fileprivate func setForegroundViewStore(_ store: [StatefulViewControllerState: [UIView]]?) {
var tempStore = store

store?.forEach { (state, views) in
tempStore?[state]?.removeDuplicates()
}

stateMachine.foregroundViewStore = tempStore
}
}

Expand All @@ -145,3 +151,17 @@ private func associatedObject<T: AnyObject>(_ host: AnyObject, key: UnsafeRawPoi
}
return value!
}

extension Array where Element: Hashable {
func removingDuplicates() -> [Element] {
var addedDict = [Element: Bool]()

return filter {
addedDict.updateValue(true, forKey: $0) == nil
}
}

mutating func removeDuplicates() {
self = self.removingDuplicates()
}
}
40 changes: 21 additions & 19 deletions StatefulViewController/ViewStateMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,17 @@ public class ViewStateMachine {
public let view: UIView

/// This store includes the views, who should be in the front during the specific state (Key)
public var foregroundViewStore: [StatefulViewControllerState: Set<UIView>]? = nil {
public var foregroundViewStore: [StatefulViewControllerState: [UIView]]? = nil {
didSet {
view.bringSubviewToFront(containerView) // Reset to the default order

if let foregroundViewStore = foregroundViewStore {
if foregroundViewStore != nil {
switch currentState {
case .none: break
case .view(let stateKey):

// Bring all stored views for the state to the front
if let state = StatefulViewControllerState(rawValue: stateKey), let foregroundViews = foregroundViewStore[state] {
for foregroundView in foregroundViews {
view.bringSubviewToFront(foregroundView)
}
}
bringForegroundViewStoreToFront(for: stateKey)
}
}
}
Expand Down Expand Up @@ -197,21 +193,16 @@ public class ViewStateMachine {
newView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(newView)

let metrics = ["top": insets.top, "bottom": insets.bottom, "left": insets.left, "right": insets.right]
let views = ["view": newView]
let hConstraints = NSLayoutConstraint.constraints(withVisualFormat: "|-left-[view]-right-|", options: [], metrics: metrics, views: views)
let vConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-top-[view]-bottom-|", options: [], metrics: metrics, views: views)
containerView.addConstraints(hConstraints)
containerView.addConstraints(vConstraints)
NSLayoutConstraint.activate([
newView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: insets.right),
newView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: insets.bottom),
newView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: insets.top),
newView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: insets.left)
])
}

// Bring all views from the foregroundViewStore, stored for the specific state, to the front
if let state = StatefulViewControllerState(rawValue: state), let foregroundViewStore = foregroundViewStore, let foregroundViews = foregroundViewStore[state] {

for foregroundView in foregroundViews {
view.bringSubviewToFront(foregroundView)
}
}
bringForegroundViewStoreToFront(for: state)

let animations: () -> () = {
if let newView = store[state] {
Expand All @@ -232,6 +223,17 @@ public class ViewStateMachine {
animateChanges(animated: animated, animations: animations, completion: animationCompletion)
}

private func bringForegroundViewStoreToFront(for state: String) {
if let stateFulVCState = StatefulViewControllerState(rawValue: state),
let foregroundViewStore = foregroundViewStore,
let foregroundViews = foregroundViewStore[stateFulVCState] {

for foregroundView in foregroundViews {
view.bringSubviewToFront(foregroundView)
}
}
}

fileprivate func hideAllViews(animated: Bool, completion: (() -> ())? = nil) {
let store = viewStore

Expand Down