Slather logo

Coverage for "EdgeAligningView.swift" : 0.00%

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