Slather logo

Coverage for "EdgeAligningView.swift" : 0.00%

(0 of 97 relevant lines covered)

ChatLayout/Classes/Extras/EdgeAligningView.swift

1
//
2
// ChatLayout
3
// EdgeAligningView.swift
4
// https://github.com/ekazaev/ChatLayout
5
//
6
// Created by Eugene Kazaev in 2020-2021.
7
// Distributed under the MIT license.
8
//
9
10
import Foundation
11
import UIKit
12
13
/// Container view that allows its `CustomView` to have lose connection to the margins of the container according to the
14
/// settings provided in `EdgeAligningView.flexibleEdges`
15
public final class EdgeAligningView<CustomView: UIView>: UIView {
16
17
    /// Represents an edge of `EdgeAligningView`
18
    public enum Edge: CaseIterable {
19
20
        /// Top edge
21
        case top
22
23
        /// Leading edge
24
        case leading
25
26
        /// Trailing edge
27
        case trailing
28
29
        /// Bottom edge
30
        case bottom
31
32
        var otherEdges: [Edge] {
!
33
            return Edge.allCases.filter { $0 != self }
!
34
        }
!
35
36
    }
37
38
    /// Set of edge constraints  to be set as loose.
39
    public var flexibleEdges: Set<Edge> = [] {
!
40
        didSet {
!
41
            guard flexibleEdges != oldValue else {
!
42
                return
!
43
            }
!
44
            setupContainer()
!
45
        }
!
46
    }
47
48
    /// Contained view.
49
    public var customView: CustomView {
50
        didSet {
!
51
            guard customView != oldValue else {
!
52
                return
!
53
            }
!
54
            oldValue.removeFromSuperview()
!
55
            setupContainer()
!
56
        }
!
57
    }
58
59
    private var addedConstraints: [NSLayoutConstraint] = []
!
60
61
    /// Initializes and returns a newly allocated `EdgeAligningView`
62
    /// - Parameters:
63
    ///   - alignedView: An instance of `CustomView`
64
    ///   - flexibleEdges: Set of edges to be set as loose.
65
    public init(with customView: CustomView, flexibleEdges: Set<Edge> = [.top]) {
!
66
        self.customView = customView
!
67
        self.flexibleEdges = flexibleEdges
!
68
        super.init(frame: customView.frame)
!
69
        setupContainer()
!
70
    }
!
71
72
    /// Initializes and returns a newly allocated view object with the specified frame rectangle.
73
    /// - Parameter frame: The frame rectangle for the view, measured in points. The origin of the frame is relative
74
    ///   to the superview in which you plan to add it.
75
    public override init(frame: CGRect) {
!
76
        self.customView = CustomView(frame: frame)
!
77
        super.init(frame: frame)
!
78
        setupSubviews()
!
79
    }
!
80
81
    @available(*, unavailable, message: "Use init(with:flexibleEdges:) instead")
82
    /// This constructor is unavailable.
83
    public required init?(coder: NSCoder) {
!
84
        fatalError("Use init(with:flexibleEdges:) instead")
!
85
    }
!
86
87
    private func setupSubviews() {
!
88
        translatesAutoresizingMaskIntoConstraints = false
!
89
        insetsLayoutMarginsFromSafeArea = false
!
90
        layoutMargins = .zero
!
91
        setupContainer()
!
92
    }
!
93
94
    private func setupContainer() {
!
95
        if customView.superview != self {
!
96
            customView.removeFromSuperview()
!
97
            addSubview(customView)
!
98
        }
!
99
        customView.translatesAutoresizingMaskIntoConstraints = false
!
100
        if !addedConstraints.isEmpty {
!
101
            removeConstraints(addedConstraints)
!
102
            addedConstraints.removeAll()
!
103
        }
!
104
        Set(Edge.allCases).subtracting(flexibleEdges).forEach { setConstraint(for: $0, on: customView, flexible: false) }
!
105
        flexibleEdges.forEach { setConstraint(for: $0, on: customView, flexible: true) }
!
106
        setDistributionConstraint(on: customView)
!
107
        setNeedsLayout()
!
108
    }
!
109
110
    private func setConstraint(for edge: Edge, on view: UIView, flexible: Bool = false) {
!
111
        var addedConstraints: [NSLayoutConstraint] = []
!
112
        switch edge {
!
113
        case .top:
!
114
            if flexible {
!
115
                addedConstraints.append(view.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor))
!
116
            } else {
!
117
                addedConstraints.append(view.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor))
!
118
            }
!
119
        case .leading:
!
120
            if flexible {
!
121
                addedConstraints.append(view.leadingAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.leadingAnchor))
!
122
            } else {
!
123
                addedConstraints.append(view.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor))
!
124
            }
!
125
        case .trailing:
!
126
            if flexible {
!
127
                addedConstraints.append(view.trailingAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.trailingAnchor))
!
128
            } else {
!
129
                addedConstraints.append(view.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor))
!
130
            }
!
131
        case .bottom:
!
132
            if flexible {
!
133
                addedConstraints.append(view.bottomAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.bottomAnchor))
!
134
            } else {
!
135
                addedConstraints.append(view.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor))
!
136
            }
!
137
        }
!
138
        addedConstraints.forEach { constraint in
!
139
            constraint.isActive = true
!
140
        }
!
141
        self.addedConstraints.append(contentsOf: addedConstraints)
!
142
    }
!
143
144
    private func setDistributionConstraint(on view: UIView) {
!
145
        if flexibleEdges.contains(.leading), flexibleEdges.contains(.trailing) {
!
146
            let layoutConstraint = view.centerXAnchor.constraint(equalTo: layoutMarginsGuide.centerXAnchor)
!
147
            addedConstraints.append(layoutConstraint)
!
148
            layoutConstraint.isActive = true
!
149
        } else if flexibleEdges.contains(.top), flexibleEdges.contains(.bottom) {
!
150
            let layoutConstraint = view.centerYAnchor.constraint(equalTo: layoutMarginsGuide.centerYAnchor)
!
151
            addedConstraints.append(layoutConstraint)
!
152
            layoutConstraint.isActive = true
!
153
        }
!
154
    }
!
155
156
}