Slather logo

Coverage for "ContainerCollectionViewCell.swift" : 0.00%

(0 of 58 relevant lines covered)

ChatLayout/Classes/Extras/ContainerCollectionViewCell.swift

1
//
2
// ChatLayout
3
// ContainerCollectionViewCell.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
/// A delegate of `ContainerCollectionViewCell`/`ContainerCollectionReusableView` should implement this methods if
14
/// it is required to participate in containers lifecycle.
15
public protocol ContainerCollectionViewCellDelegate: AnyObject {
16
17
    /// Perform any clean up necessary to prepare the view for use again.
18
    func prepareForReuse()
19
20
    /// Allows to override the call of `ContainerCollectionViewCell`/`ContainerCollectionReusableView`
21
    /// `UICollectionReusableView.preferredLayoutAttributesFitting(...)` and make the layout calculations.
22
    ///
23
    /// **NB**: You must override it to avoid unnecessary autolayout calculations if you are providing exact cell size
24
    /// in `ChatLayoutDelegate.sizeForItem(...)` and return `layoutAttributes` without modifications.
25
    /// - Parameter layoutAttributes: `ChatLayoutAttributes` provided by `ChatLayout`
26
    /// - Returns: Modified `ChatLayoutAttributes` on nil if `UICollectionReusableView.preferredLayoutAttributesFitting(...)`
27
    ///            should be called instead.
28
    func preferredLayoutAttributesFitting(_ layoutAttributes: ChatLayoutAttributes) -> ChatLayoutAttributes?
29
30
    /// Allows to additionally modify `ChatLayoutAttributes` after the `UICollectionReusableView.preferredLayoutAttributesFitting(...)`
31
    /// call.
32
    /// - Parameter layoutAttributes: `ChatLayoutAttributes` provided by `ChatLayout`.
33
    /// - Returns: Modified `ChatLayoutAttributes`
34
    func modifyPreferredLayoutAttributesFitting(_ layoutAttributes: ChatLayoutAttributes)
35
36
    /// Apply the specified layout attributes to the view.
37
    /// Keep in mind that this method can be called multiple times.
38
    /// - Parameter layoutAttributes: `ChatLayoutAttributes` provided by `ChatLayout`.
39
    func apply(_ layoutAttributes: ChatLayoutAttributes)
40
41
}
42
43
/// Default extension to make the methods optional for implementation in the successor
44
public extension ContainerCollectionViewCellDelegate {
45
46
    func prepareForReuse() {}
!
47
48
    func preferredLayoutAttributesFitting(_ layoutAttributes: ChatLayoutAttributes) -> ChatLayoutAttributes? {
!
49
        return nil
!
50
    }
!
51
52
    func modifyPreferredLayoutAttributesFitting(_ layoutAttributes: ChatLayoutAttributes) {}
!
53
54
    func apply(_ layoutAttributes: ChatLayoutAttributes) {}
!
55
56
}
57
58
/// A container `UICollectionViewCell` that constraints its contained view to its margins.
59
public final class ContainerCollectionViewCell<CustomView: UIView>: UICollectionViewCell {
60
61
    /// Default reuse identifier is set with the class name.
62
    public static var reuseIdentifier: String {
!
63
        return String(describing: self)
!
64
    }
!
65
66
    /// Contained view.
67
    public lazy var customView = CustomView(frame: bounds)
68
69
    /// An instance of `ContainerCollectionViewCellDelegate`
70
    public weak var delegate: ContainerCollectionViewCellDelegate?
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
    override init(frame: CGRect) {
!
76
        super.init(frame: frame)
!
77
        setupSubviews()
!
78
    }
!
79
80
    @available(*, unavailable, message: "Use init(reuseIdentifier:) instead")
81
    /// This constructor is unavailable.
82
    public required init?(coder aDecoder: NSCoder) {
!
83
        fatalError("init(coder:) has not been implemented")
!
84
    }
!
85
86
    /// Performs any clean up necessary to prepare the view for use again.
87
    public override func prepareForReuse() {
!
88
        super.prepareForReuse()
!
89
        delegate?.prepareForReuse()
!
90
    }
!
91
92
    /// Gives the cell a chance to modify the attributes provided by the layout object.
93
    /// - Parameter layoutAttributes: The attributes provided by the layout object. These attributes represent the values that the layout intends to apply to the cell.
94
    /// - Returns: Modified `UICollectionViewLayoutAttributes`
95
    public override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
!
96
        guard let chatLayoutAttributes = layoutAttributes as? ChatLayoutAttributes else {
!
97
            return super.preferredLayoutAttributesFitting(layoutAttributes)
!
98
        }
!
99
        delegate?.apply(chatLayoutAttributes)
!
100
        let resultingLayoutAttributes: ChatLayoutAttributes
!
101
        if let preferredLayoutAttributes = delegate?.preferredLayoutAttributesFitting(chatLayoutAttributes) {
!
102
            resultingLayoutAttributes = preferredLayoutAttributes
!
103
        } else if let chatLayoutAttributes = super.preferredLayoutAttributesFitting(chatLayoutAttributes) as? ChatLayoutAttributes {
!
104
            delegate?.modifyPreferredLayoutAttributesFitting(chatLayoutAttributes)
!
105
            resultingLayoutAttributes = chatLayoutAttributes
!
106
        } else {
!
107
            resultingLayoutAttributes = chatLayoutAttributes
!
108
        }
!
109
        return resultingLayoutAttributes
!
110
    }
!
111
112
    /// Applies the specified layout attributes to the view.
113
    /// - Parameter layoutAttributes: The layout attributes to apply.
114
    public override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
!
115
        guard let chatLayoutAttributes = layoutAttributes as? ChatLayoutAttributes else {
!
116
            return
!
117
        }
!
118
        super.apply(layoutAttributes)
!
119
        delegate?.apply(chatLayoutAttributes)
!
120
    }
!
121
122
    private func setupSubviews() {
!
123
        contentView.addSubview(customView)
!
124
        translatesAutoresizingMaskIntoConstraints = false
!
125
        insetsLayoutMarginsFromSafeArea = false
!
126
        layoutMargins = .zero
!
127
!
128
        contentView.insetsLayoutMarginsFromSafeArea = false
!
129
        contentView.layoutMargins = .zero
!
130
!
131
        customView.translatesAutoresizingMaskIntoConstraints = false
!
132
        customView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor).isActive = true
!
133
        customView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor).isActive = true
!
134
        customView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor).isActive = true
!
135
        customView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor).isActive = true
!
136
    }
!
137
138
}