-
Notifications
You must be signed in to change notification settings - Fork 3
/
Layout.swift
93 lines (80 loc) · 3 KB
/
Layout.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//
// Layout.swift
// JSONLayout
//
// Created by Maxim Volgin on 10/02/2019.
// Copyright © 2019 Maxim Volgin. All rights reserved.
//
import UIKit
public enum LayoutError: Error {
case fileNotFound(String)
case classNotFound(String)
}
public typealias ViewOfTypeForID = (String, String) -> UIView?
public typealias DidCreateViewForID = (UIView, String) -> Void
public typealias FormatOptionsForConstraintID = (String) -> NSLayoutConstraint.FormatOptions
public class Layout {
private var views: [String: UIView] = [:]
private var metrics: [String: Float] = [:]
private var constraints = [NSLayoutConstraint]()
private let layout: MarkupLayout
public init(name: String) throws {
guard let url = Bundle.main.url(forResource: name, withExtension: "json") else {
throw LayoutError.fileNotFound("\(name).json")
}
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
self.layout = try decoder.decode(MarkupLayout.self, from: data)
}
private func addConstraint(id: String, visualFormat: String) {
self.constraints += NSLayoutConstraint.constraints(
withVisualFormat: visualFormat,
options: self.formatOptions(id),
metrics: metrics,
views: views)
}
private func add(views: [String: MarkupElement], to root: UIView) throws {
for (id, view) in views {
guard let v = self.viewOfType(view.type, id) else {
throw LayoutError.classNotFound(view.type)
}
self.didCreateView(v, id)
v.translatesAutoresizingMaskIntoConstraints = false
v.tag = id.hash
self.views[id] = v
root.addSubview(v)
if let children = view.children {
// recursion
try self.add(views: children, to: v)
}
}
}
public func inflate(in root: UIView) throws {
root.translatesAutoresizingMaskIntoConstraints = false
try self.add(views: layout.views, to: root)
self.metrics = layout.metrics
for (id, constraint) in layout.constraints {
self.addConstraint(id: id, visualFormat: constraint)
}
NSLayoutConstraint.activate(self.constraints)
root.layoutSubviews()
}
public static func view(of type: String) -> UIView? {
if let cls = NSClassFromString(type) as? UIView.Type {
let v = cls.init()
return v
}
return nil
}
// MARK: - configuration
public var formatOptions: FormatOptionsForConstraintID = { (id) -> NSLayoutConstraint.FormatOptions in [] }
public var viewOfType: ViewOfTypeForID = { (type, id) -> UIView? in return Layout.view(of: type) }
public var didCreateView: DidCreateViewForID = { (view, id) -> Void in return }
}
extension Layout {
@discardableResult
public func configure(_ closure: (Layout) -> Void) -> Layout {
closure(self)
return self
}
}