### Setup Subviews Method Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/EdgeAligningView.swift.html Configures the initial setup for the EdgeAligningView's subviews, including disabling autoresizing masks and setting layout margins. ```swift private func setupSubviews() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false layoutMargins = .zero setupContainer() } ``` -------------------------------- ### Initialize LayoutModel Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/LayoutModel.swift.html Initializes the LayoutModel with sections and a collection layout reference. This is a core setup method for the model. ```swift final class LayoutModel { private struct ItemUUIDKey: Hashable { let kind: ItemKind let id: UUID } private(set) var sections: ContiguousArray> private unowned var collectionLayout: Layout private var sectionIndexByIdentifierCache: [UUID: Int]? private var itemPathByIdentifierCache: [ItemUUIDKey: ItemPath]? init(sections: ContiguousArray>, collectionLayout: Layout) { self.sections = sections self.collectionLayout = collectionLayout } // ... other methods ``` -------------------------------- ### Setup Container Method Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/EdgeAligningView.swift.html Configures the constraints for the custom view within the EdgeAligningView. It adds or removes constraints based on the flexibleEdges property. ```swift private func setupContainer() { if customView.superview != self { customView.removeFromSuperview() addSubview(customView) } customView.translatesAutoresizingMaskIntoConstraints = false if !addedConstraints.isEmpty { removeConstraints(addedConstraints) addedConstraints.removeAll() } Set(Edge.allCases).subtracting(flexibleEdges).forEach { setConstraint(for: $0, on: customView, flexible: false) } flexibleEdges.forEach { setConstraint(for: $0, on: customView, flexible: true) } setDistributionConstraint(on: customView) setNeedsLayout() } ``` -------------------------------- ### Setup CellLayoutContainerView Subviews Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CellLayoutContainerView.swift.html Sets up the initial subview properties for CellLayoutContainerView, including disabling autoresizingMaskIntoConstraints and insetsLayoutMarginsFromSafeArea. ```swift private func setupSubviews() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false } ``` -------------------------------- ### ImageMaskedView Setup Mask Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/ImageMaskedView.swift.html Sets up the image view and applies it as a mask to the ImageMaskedView. If no masking image is provided, the mask is removed. ```swift private func setupMask() { guard let bubbleImage = maskingImage else { imageView.image = nil mask = nil return } imageView.image = bubbleImage mask = imageView updateMask() } ``` -------------------------------- ### Setup Subviews for MessageContainerView Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/MessageContainerView.swift.html Configures the subviews, including the stack view and internal content view, and sets up their constraints. This method is called during initialization. ```swift private func setupSubviews() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false layoutMargins = .zero addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis = .horizontal stackView.spacing = .zero NSLayoutConstraint.activate([ stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor), stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor), stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor), stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor) ]) if let accessoryView { stackView.addArrangedSubview(accessoryView) accessoryView.translatesAutoresizingMaskIntoConstraints = false } internalContentView.translatesAutoresizingMaskIntoConstraints = false stackView.addArrangedSubview(internalContentView) } ``` -------------------------------- ### Initial Layout Attributes for Appearing Item Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Retrieves the starting layout attributes for an item that is being inserted into the collection view, allowing for custom entry animations. ```APIDOC ## override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? ### Description Retrieves the starting layout information for an item being inserted into the collection view. This method is used to define the initial state of an appearing item, enabling custom animations such as fading in or sliding. ### Method `override func` ### Endpoint N/A (This is a method within a UICollectionViewLayout subclass) ### Parameters - **itemIndexPath** (IndexPath) - The index path of the item for which to retrieve initial layout attributes. ### Request Example N/A ### Response - **UICollectionViewLayoutAttributes?** - The initial layout attributes for the appearing item, or `nil` if no special attributes are needed. #### Success Response (200) - **UICollectionViewLayoutAttributes?** (Optional) - The initial layout attributes. #### Response Example ```json { "example": "// Returns initial layout attributes or nil" } ``` ``` -------------------------------- ### additionalInsets Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/ChatLayoutAttributes.html CollectionViewChatLayout's additional insets setup using ChatLayoutSettings. Added for convenience. ```APIDOC ## additionalInsets ### Description `[CollectionViewChatLayout](../Classes/CollectionViewChatLayout.html)`s additional insets setup using `[ChatLayoutSettings](../Structs/ChatLayoutSettings.html)`. Added for convenience. ### Declaration ```swift @MainActor public internal(set) var additionalInsets: UIEdgeInsets { get } ``` ``` -------------------------------- ### ImageMaskedView Setup Subviews Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/ImageMaskedView.swift.html Configures the initial layout and subviews for ImageMaskedView. It adds the custom view and sets up constraints to fill the layout margins. ```swift private func setupSubviews() { layoutMargins = .zero translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false addSubview(customView) customView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ customView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor), customView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor), customView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor), customView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor) ]) } ``` -------------------------------- ### Get Number of Sections Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Returns the total number of sections in the layout for the given state. ```swift layout(at: state).sections.count ``` -------------------------------- ### Initial Layout Attributes for Appearing Item Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/CollectionViewChatLayout.html Provides the starting layout attributes for an item that is being inserted into the collection view. This is useful for animating the appearance of new cells. ```Swift @MainActor open override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? ``` -------------------------------- ### initialLayoutAttributesForAppearingItem(at:) Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/CollectionViewChatLayout.html Retrieves the starting layout information for an item being inserted into the collection view. This method is part of the cell appearance animation process. ```APIDOC ## initialLayoutAttributesForAppearingItem(at:) ### Description Retrieves the starting layout information for an item being inserted into the collection view. ### Method Swift ### Endpoint N/A (Objective-C/Swift method) ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example N/A ### Response #### Success Response - `UICollectionViewLayoutAttributes?`: The layout attributes for the appearing item, or nil if no animation is needed. ``` -------------------------------- ### Initial Layout for Appearing Item Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Retrieves the starting layout information for an item being inserted into the collection view. It handles animations for inserted or reloaded items, applying alpha and offset adjustments as needed. ```swift public override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? { var attributes: ChatLayoutAttributes? let itemPath = itemIndexPath.itemPath if state == .afterUpdate { if controller.insertedIndexes.contains(itemIndexPath) || controller.insertedSectionsIndexes.contains(itemPath.section) { attributes = controller.itemAttributes(for: itemPath, kind: .cell, at: .afterUpdate)?.typedCopy() controller.offsetByTotalCompensation(attributes: attributes, for: state, backward: true) attributes.map { attributes in guard let delegate else { attributes.alpha = 0 return } delegate.initialLayoutAttributesForInsertedItem(self, of: .cell, at: itemIndexPath, modifying: attributes, on: .initial) } attributesForPendingAnimations[.cell]?[itemPath] = attributes } else if let itemIdentifier = controller.itemIdentifier(for: itemPath, kind: .cell, at: .afterUpdate), let initialIndexPath = controller.itemPath(by: itemIdentifier, kind: .cell, at: .beforeUpdate) { attributes = controller.itemAttributes(for: initialIndexPath, kind: .cell, at: .beforeUpdate)?.typedCopy() ?? ChatLayoutAttributes(forCellWith: itemIndexPath) attributes?.indexPath = itemIndexPath if #unavailable(iOS 13.0) { if controller.reloadedIndexes.contains(itemIndexPath) || controller.reloadedSectionsIndexes.contains(itemPath.section) { attributesForPendingAnimations[.cell]?[itemPath] = attributes } } } else { attributes = controller.itemAttributes(for: itemPath, kind: .cell, at: .beforeUpdate) } } else { attributes = controller.itemAttributes(for: itemPath, kind: .cell, at: .beforeUpdate) } return attributes } ``` -------------------------------- ### ContainerCollectionViewCell Setup Subviews Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/ContainerCollectionViewCell.swift.html Configures the cell's content view and adds the custom view as a subview, constraining it to the content view's layout margins. ```swift private func setupSubviews() { contentView.addSubview(customView) insetsLayoutMarginsFromSafeArea = false layoutMargins = .zero contentView.insetsLayoutMarginsFromSafeArea = false contentView.layoutMargins = .zero customView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ customView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor), customView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor), ]) } ``` -------------------------------- ### initialLayoutAttributesForAppearingItem Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Classes/CollectionViewChatLayout.html Retrieves the starting layout information for an item being inserted into the collection view. This allows for custom animations when new items appear. ```APIDOC ## initialLayoutAttributesForAppearingItem(at:) ### Description Retrieves the starting layout information for an item being inserted into the collection view. ### Declaration ```swift @MainActor open override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? ``` ``` -------------------------------- ### Leading Accessory View Property Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Classes/CellLayoutContainerView.html Represents the leading accessory view. Use this property to set or get the leading accessory view for the container. ```swift @MainActor public lazy var leadingView: LeadingAccessory.View? { get set } ``` -------------------------------- ### Assemble Chat Layout Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/LayoutModel.swift.html Assembles the entire chat layout by calculating section offsets and item paths. This method should be called after initial setup or significant data changes. ```swift func assembleLayout() { var offsetY: CGFloat = collectionLayout.settings.additionalInsets.top var sectionIndexByIdentifierCache = [UUID: Int](minimumCapacity: sections.count) var itemPathByIdentifierCache = [ItemUUIDKey: ItemPath](minimumCapacity: sections.reduce(into: 0) { $0 += $1.items.count }) sections.withUnsafeMutableBufferPointer { directlyMutableSections in for sectionIndex in 0.. ChatLayoutPositionSnapshot?` ### Parameters #### Path Parameters - **edge** (ChatLayoutPositionSnapshot.Edge) - The edge of the `UICollectionView` ``` -------------------------------- ### prepare() Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Classes/CollectionViewChatLayout.html Tells the layout object to update the current layout. ```APIDOC ## prepare() ### Description Tells the layout object to update the current layout. ### Method `@MainActor open override func prepare()` ``` -------------------------------- ### Prepare Actions OptionSet Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html An OptionSet defining actions to be performed during the prepare phase of the layout. ```swift private struct PrepareActions: OptionSet { let rawValue: UInt static let recreateSectionModels = PrepareActions(rawValue: 1 << 0) static let updateLayoutMetrics = PrepareActions(rawValue: 1 << 1) static let cachePreviousWidth = PrepareActions(rawValue: 1 << 2) static let cachePreviousContentInsets = PrepareActions(rawValue: 1 << 3) static let switchStates = PrepareActions(rawValue: 1 << 4) } ``` -------------------------------- ### Get Item Path by ID Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Retrieves the ItemPath for a given item ID, kind, and state. ```swift layout(at: state).itemPath(by: itemId, kind: kind) ``` -------------------------------- ### SwappingContainerView Initializers Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/SwappingContainerView.html Provides documentation for the initializers of SwappingContainerView, including those with custom frame, axis, distribution, spacing, and preferred priority, as well as the default frame initializer. ```APIDOC ## Initializers ### `init(frame:axis:distribution:spacing:preferredPriority:)` Initializes and returns a newly allocated view object with the specified frame rectangle. #### Parameters - **frame** (CGRect) - The frame rectangle for the view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. - **axis** (Axis) - The view distribution axis. Defaults to `.horizontal`. - **distribution** (Distribution) - The layout of the arranged subviews along the axis. Defaults to `.accessoryFirst`. - **spacing** (CGFloat) - The distance in points between the edges of the contained views. - **preferredPriority** (UILayoutPriority) - Preferred priority of the internal constraints. Defaults to `.required`. ### `init(frame:)` Initializes and returns a newly allocated view object with the specified frame rectangle. #### Parameters - **frame** (CGRect) - The frame rectangle for the view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. ``` -------------------------------- ### init(frame:) Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Classes/CellLayoutContainerView.html Initializes and returns a newly allocated view object with the specified frame rectangle. ```APIDOC ## init(frame:) ### Description Initializes and returns a newly allocated view object with the specified frame rectangle. ### Declaration ```swift @MainActor public override init(frame: CGRect) ``` ### Parameters #### frame - **frame** (_CGRect_) - The frame rectangle for the view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. ``` -------------------------------- ### prepare(forAnimatedBoundsChange:) Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Classes/CollectionViewChatLayout.html Prepares the layout object for animated changes to the view’s bounds or the insertion or deletion of items. ```APIDOC ## prepare(forAnimatedBoundsChange:) ### Description Prepares the layout object for animated changes to the view’s bounds or the insertion or deletion of items. ### Method `@MainActor open override func prepare(forAnimatedBoundsChange oldBounds: CGRect)` ### Parameters #### Path Parameters - **oldBounds** (CGRect) - ``` -------------------------------- ### Edge Enum Definition Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/EdgeAligningView.swift.html Defines the possible edges of an EdgeAligningView and provides a computed property to get all other edges. ```swift public enum Edge: CaseIterable { /// Top edge case top /// Leading edge case leading /// Trailing edge case trailing /// Bottom edge case bottom var otherEdges: [Edge] { Edge.allCases.filter { $0 != self } } } ``` -------------------------------- ### ChatLayout Initialization Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Provides details on the constructors available for initializing the ChatLayout class, including default and coder-based initializations. ```APIDOC ## ChatLayout Constructors ### Default constructor. - Parameters: - `flipsHorizontallyInOppositeLayoutDirection` (Bool) - Optional - Indicates whether the horizontal coordinate system is automatically flipped at appropriate times. In practice, this is used to support right-to-left layout. ```swift public init(flipsHorizontallyInOppositeLayoutDirection: Bool = true) ``` ### Coder constructor. - Returns: An object initialized from data in a given unarchiver. ```swift public required init?(coder aDecoder: NSCoder) ``` ``` -------------------------------- ### Get Item Alignment Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Protocols/ChatLayoutDelegate.html Asks the delegate for the alignment type of a specific chat item. The default implementation returns `ChatItemAlignment.fullWidth`. ```swift @MainActor func alignmentForItem( _ chatLayout: CollectionViewChatLayout, at indexPath: IndexPath ) -> ChatItemAlignment ``` -------------------------------- ### Prepare Actions State Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Holds the current set of prepare actions. ```swift private var prepareActions: PrepareActions = [] ``` -------------------------------- ### prepareForReuse() Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/ContainerCollectionReusableView.html Performs any clean up necessary to prepare the view for use again. ```APIDOC ## prepareForReuse() ### Description Performs any clean up necessary to prepare the view for use again. ### Declaration ```swift @MainActor public override func prepareForReuse() ``` ``` -------------------------------- ### Get Number of Items in Section Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Returns the number of items within a specific section, identified by its index, for the given state. ```swift layout(at: state).sections[sectionIndex].items.count ``` -------------------------------- ### Initialization with Coder Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/MessageContainerView.html Initializes a MessageContainerView from data in a given unarchiver. This is required for NSCoding compliance. ```swift @MainActor public required init?(coder: NSCoder) ``` -------------------------------- ### Get ItemSize CaseType Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/ItemSize.swift.html Returns the CaseType for the current ItemSize. This method helps in determining the type of size calculation being used. ```swift public var caseType: CaseType { switch self { case .auto: return .auto case .estimated: return .estimated case .exact: return .exact } } ``` -------------------------------- ### CollectionViewChatLayout Constructors Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/CollectionViewChatLayout.html Details the available constructors for initializing a CollectionViewChatLayout object. ```APIDOC ## Constructors ### `init(flipsHorizontallyInOppositeLayoutDirection:)` **Description**: Default constructor. **Declaration**: ```swift @MainActor public init(flipsHorizontallyInOppositeLayoutDirection: Bool = true) ``` **Parameters**: * `_flipsHorizontallyInOppositeLayoutDirection_` (Bool) - Indicates whether the horizontal coordinate system is automatically flipped at appropriate times. In practice, this is used to support right-to-left layout. ### `init(coder:)` **Description**: Returns an object initialized from data in a given unarchiver. **Declaration**: ```swift @MainActor public required init?(coder aDecoder: NSCoder) ``` ``` -------------------------------- ### Get Raw Value for ChangeItem Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/ChangeItem.swift.html Provides a raw integer value for each case of the ChangeItem enum, useful for internal processing or mapping. ```swift private var rawValue: Int { switch self { case .sectionReload: return 0 case .itemReload: return 1 case .sectionDelete: return 2 case .itemDelete: return 3 case .sectionInsert: return 4 ``` -------------------------------- ### CellLayoutContainerView Initializers Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/CellLayoutContainerView.html Details the available initializers for creating instances of CellLayoutContainerView. ```APIDOC ## CellLayoutContainerView Initializers ### Description Provides methods for initializing a `CellLayoutContainerView` instance. ### Initializers - **init(frame:)** Initializes and returns a newly allocated view object with the specified frame rectangle. #### Parameters - **frame** (CGRect) - The frame rectangle for the view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. - **init?(coder:)** Returns an object initialized from data in a given unarchiver. #### Parameters - **coder** (NSCoder?) - An unarchiver object. ``` -------------------------------- ### State Controller Initialization Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Initializes the StateController with the layout representation. ```swift private lazy var controller = StateController(layoutRepresentation: self) ``` -------------------------------- ### Get Number of Items in Section Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Returns the number of items in a given section of the collection view. Returns zero if the collection view is not available. ```swift func numberOfItems(in section: Int) -> Int { guard let collectionView else { return .zero } return collectionView.numberOfItems(inSection: section) } ``` -------------------------------- ### Trailing Accessory View Property Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Classes/CellLayoutContainerView.html Represents the trailing accessory view. Use this property to set or get the trailing accessory view for the container. ```swift @MainActor public lazy var trailingView: TrailingAccessory.View? { get set } ``` -------------------------------- ### Swift - Coder Initializer Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Classes/CollectionViewChatLayout.html Initializes the layout from data in a given unarchiver. This is a required initializer for NSCoding compliance. ```swift @MainActor public required init?(coder aDecoder: NSCoder) ``` -------------------------------- ### Initializer with Custom View and Flexible Edges Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/EdgeAligningView.swift.html Initializes an EdgeAligningView with a custom view and a set of flexible edges. Defaults to top edge being flexible. ```swift public init(with customView: CustomView, flexibleEdges: Set = [.top]) { self.customView = customView self.flexibleEdges = flexibleEdges super.init(frame: customView.frame) setupContainer() } ``` -------------------------------- ### RoundedCornersContainerView Corner Radius Property Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/RoundedCornersContainerView.html Sets or gets the corner radius for the rounded corners. If not provided, the half of the current view height will be used. ```swift @MainActor public var cornerRadius: CGFloat? ``` -------------------------------- ### Initialize StateController Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Initializes the StateController with a layout representation. It sets up the initial layout model before any updates and resets cached attribute objects. ```swift final class StateController { // ... (other properties and methods) private var layoutBeforeUpdate: LayoutModel private var layoutAfterUpdate: LayoutModel? private unowned var layoutRepresentation: Layout init(layoutRepresentation: Layout) { self.layoutRepresentation = layoutRepresentation layoutBeforeUpdate = LayoutModel(sections: [], collectionLayout: self.layoutRepresentation) resetCachedAttributeObjects() } // ... (other methods) } ``` -------------------------------- ### Get Section Model Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Retrieves the SectionModel for a given index and state. Includes a debug assertion to ensure the index is within the valid range of sections. ```swift #if DEBUG guard index < layout(at: state).sections.count else { preconditionFailure("Section index \(index) is bigger than the amount of sections \(layout(at: state).sections.count).") } #endif return layout(at: state).sections[index] ``` -------------------------------- ### customLeadingSpacing Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Classes/CellLayoutContainerView.html Custom spacing between the leading and main views. This allows for specific spacing adjustments between the leading accessory and the main content. ```APIDOC ## customLeadingSpacing ### Description Custom spacing between the leading and main views. ### Declaration ```swift @MainActor public var customLeadingSpacing: CGFloat { get set } ``` ``` -------------------------------- ### Prepare for Animated Bounds Change Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Prepares the layout for animated changes to bounds or item insertions/deletions. It updates internal state and compensates for content offset changes. ```swift public override func prepare(forAnimatedBoundsChange oldBounds: CGRect) { controller.isAnimatedBoundsChange = true controller.process(changeItems: []) state = .afterUpdate prepareActions.remove(.switchStates) guard let collectionView, oldBounds.width != collectionView.bounds.width, keepContentOffsetAtBottomOnBatchUpdates, controller.isLayoutBiggerThanVisibleBounds(at: state) else { return } let newBounds = collectionView.bounds let heightDifference = oldBounds.height - newBounds.height controller.proposedCompensatingOffset += heightDifference + (oldBounds.origin.y - newBounds.origin.y) } ``` -------------------------------- ### Get Content Offset Snapshot from Edge Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Retrieves the offset of the item closest to a specified edge of the UICollectionView. Requires a valid collectionView and layout attributes. ```swift public func getContentOffsetSnapshot(from edge: ChatLayoutPositionSnapshot.Edge) -> ChatLayoutPositionSnapshot? { guard let collectionView else { return nil } let insets = UIEdgeInsets(top: -collectionView.frame.height, left: 0, bottom: -collectionView.frame.height, right: 0) let visibleBounds = visibleBounds let layoutAttributes = controller.layoutAttributesForElements(in: visibleBounds.inset(by: insets), state: state, ignoreCache: true) .sorted(by: { $0.frame.maxY < $1.frame.maxY }) switch edge { case .top: guard let firstVisibleItemAttributes = layoutAttributes.first(where: { $0.frame.minY >= visibleBounds.higherPoint.y }) else { return nil } let visibleBoundsTopOffset = firstVisibleItemAttributes.frame.minY - visibleBounds.higherPoint.y - settings.additionalInsets.top return ChatLayoutPositionSnapshot(indexPath: firstVisibleItemAttributes.indexPath, kind: firstVisibleItemAttributes.kind, edge: .top, offset: visibleBoundsTopOffset) case .bottom: guard let lastVisibleItemAttributes = layoutAttributes.last(where: { $0.frame.minY <= visibleBounds.lowerPoint.y }) else { return nil } let visibleBoundsBottomOffset = visibleBounds.lowerPoint.y - lastVisibleItemAttributes.frame.maxY - settings.additionalInsets.bottom return ChatLayoutPositionSnapshot(indexPath: lastVisibleItemAttributes.indexPath, kind: lastVisibleItemAttributes.kind, edge: .bottom, offset: visibleBoundsBottomOffset) } } ``` -------------------------------- ### apply(_:) Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/ContainerCollectionReusableView.html Applies the specified layout attributes to the view. ```APIDOC ## apply(_:) ### Description Applies the specified layout attributes to the view. ### Declaration ```swift @MainActor public override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) ``` ### Parameters #### `_layoutAttributes_` - **layoutAttributes** (UICollectionViewLayoutAttributes) - The layout attributes to apply. ``` -------------------------------- ### Initialize ChatLayoutSettings Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Initializes ChatLayoutSettings with default values. This object holds various settings that can be customized to alter the layout's behavior. ```swift public var settings = ChatLayoutSettings() ``` -------------------------------- ### Set Center Distribution Constraint Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/EdgeAligningView.swift.html Sets a constraint to center a view horizontally or vertically within the layout margins guide. Use when both opposing edges are flexible. ```swift let layoutConstraint = view.centerXAnchor.constraint(equalTo: layoutMarginsGuide.centerXAnchor) ``` ```swift layoutConstraint.isActive = true ``` ```swift let layoutConstraint = view.centerYAnchor.constraint(equalTo: layoutMarginsGuide.centerYAnchor) ``` -------------------------------- ### Get Item Alignment in Chat Layout Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Retrieves the alignment for a chat item based on the delegate's configuration. Defaults to full width if no delegate is set. ```swift private func alignment(for element: ItemKind, at indexPath: IndexPath) -> ChatItemAlignment { guard let delegate else { return .fullWidth } return delegate.alignmentForItem(self, of: element, at: indexPath) } ``` -------------------------------- ### copy(with:) Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/ChatLayoutAttributes.html Returns an exact copy of ChatLayoutAttributes. ```APIDOC ## copy(with:) ### Description Returns an exact copy of `ChatLayoutAttributes`. ### Declaration ```swift @MainActor public override func copy(with zone: NSZone? = nil) -> Any ``` ``` -------------------------------- ### Get Section Index by Identifier Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Retrieves the index for a given section identifier and state. Returns nil if the section is not found, which can occur during initial or final animations. ```swift guard let sectionIndex = layout(at: state).sectionIndex(by: sectionIdentifier) else { // This occurs when getting layout attributes for initial / final animations return nil } return sectionIndex ``` -------------------------------- ### Initialize ChatLayout from Coder Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Required initializer for unarchiving ChatLayout objects. Defaults horizontal flipping to true. ```swift public required init?(coder aDecoder: NSCoder) { _flipsHorizontallyInOppositeLayoutDirection = true super.init(coder: aDecoder) resetAttributesForPendingAnimations() resetInvalidatedAttributes() } ``` -------------------------------- ### Get Section Identifier Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Retrieves the UUID identifier for a section at a given index and state. Returns nil if the index is out of bounds, which can occur during initial or final animations. ```swift let layout = layout(at: state) guard index < layout.sections.count else { // This occurs when getting layout attributes for initial / final animations return nil } return layout.sections[index].id ``` -------------------------------- ### Prepare for Collection View Updates Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Handles preparation actions before collection view updates, including caching content insets and view sizes. ```swift if prepareActions.contains(.cachePreviousContentInsets) { cachedCollectionViewInset = adjustedContentInset } ! if prepareActions.contains(.cachePreviousWidth) { cachedCollectionViewSize = collectionView.bounds.size } ! prepareActions = [] ``` -------------------------------- ### Get Section Index by ID Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/LayoutModel.swift.html Retrieves the index of a section using its UUID. It utilizes a cache for efficiency, falling back to a linear search if the cache is not prepared. ```swift func sectionIndex(by sectionId: UUID) -> Int? { guard let sectionIndexByIdentifierCache else { assertionFailure("Internal inconsistency. Cache is not prepared.") return sections.firstIndex(where: { $0.id == sectionId }) } return sectionIndexByIdentifierCache[sectionId] } ``` -------------------------------- ### Configuration for Item Kind and IndexPath Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Calculates the estimated size for a given item kind and index path within the collection view layout. ```swift let itemSize = estimatedSize(for: element, at: indexPath) ``` -------------------------------- ### Get Layout Frame Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Calculates and returns the main rectangle where all items are aligned, incorporating additional insets defined in the settings. This defines the working area for the layout. ```swift public var layoutFrame: CGRect { guard let collectionView else { return .zero } let additionalInsets = settings.additionalInsets return CGRect(x: adjustedContentInset.left + additionalInsets.left, y: adjustedContentInset.top + additionalInsets.top, width: collectionView.bounds.width - adjustedContentInset.left - adjustedContentInset.right, height: collectionView.bounds.height - adjustedContentInset.top - adjustedContentInset.bottom) } ``` -------------------------------- ### MessageContainerView Initializers Source: https://github.com/ekazaev/chatlayout/blob/master/docs/docsets/ChatLayout.docset/Contents/Resources/Documents/Classes/MessageContainerView.html Initializers for the MessageContainerView class. ```APIDOC ## MessageContainerView Initializers ### init(frame:) Initializes and returns a newly allocated view object with the specified frame rectangle. #### Declaration ```swift @MainActor public override init(frame: CGRect) ``` #### Parameters - `frame`: The frame rectangle for the view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. ### init(coder:) Returns an object initialized from data in a given unarchiver. #### Declaration ```swift @MainActor public required init?(coder: NSCoder) ``` #### Parameters - `coder`: An unarchiver object. ``` -------------------------------- ### Initialize RoundedCornersContainerView Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/RoundedCornersContainerView.swift.html Initializes the container view. Use this when creating an instance programmatically. Requires setupSubviews() to be called. ```swift public override init(frame: CGRect) { super.init(frame: frame) setupSubviews() } ``` -------------------------------- ### StaticViewFactory Extension for UIView Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Protocols/StaticViewFactory.html Provides a default implementation for `buildView(within:)` for any `UIView` conforming to `StaticViewFactory`, using its default constructor. ```APIDOC ## Extension: StaticViewFactory (where Self: UIView) ### Description Default extension to build the `UIView` using its default constructor. ### Methods #### `buildView(within:)` - **Declaration**: `static func buildView(within bounds: CGRect) -> Self?` - **Return Value**: An optional instance of the `UIView`. ``` -------------------------------- ### Get Pinned Item Index Path Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/CollectionViewChatLayout.html Returns the index path of an item that is currently pinned according to the specified pinning type. Useful for identifying fixed elements. ```swift @MainActor open func indexPathForItemPinnedAt(_ pinningType: ChatItemPinningType) -> IndexPath? ``` -------------------------------- ### Get Content Offset Snapshot Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/CollectionViewChatLayout.html Retrieves a snapshot of the current content offset, focusing on the item closest to a specified edge. Useful for saving and restoring scroll positions. ```swift @MainActor open func getContentOffsetSnapshot(from edge: ChatLayoutPositionSnapshot.Edge) -> ChatLayoutPositionSnapshot? ``` -------------------------------- ### Custom Methods Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/CollectionViewChatLayout.html Provides methods for snapshotting and restoring content offsets, reconfiguring items, and retrieving pinned item index paths. ```APIDOC ## getContentOffsetSnapshot(from:) ### Description Get current offset of the item closest to the provided edge. ### Method `@MainActor open func getContentOffsetSnapshot(from edge: ChatLayoutPositionSnapshot.Edge) -> ChatLayoutPositionSnapshot?` ### Parameters #### Path Parameters - **edge** (ChatLayoutPositionSnapshot.Edge) - Required - The edge of the `UICollectionView` ### Return Value `ChatLayoutPositionSnapshot?` ``` ```APIDOC ## restoreContentOffset(with:) ### Description Invalidates layout of the `UICollectionView` and trying to keep the offset of the item provided in `ChatLayoutPositionSnapshot`. ### Method `@MainActor open func restoreContentOffset(with snapshot: ChatLayoutPositionSnapshot)` ### Parameters #### Path Parameters - **snapshot** (ChatLayoutPositionSnapshot) - Required - `ChatLayoutPositionSnapshot` ``` ```APIDOC ## reconfigureItems(at:) ### Description If you want to use new `UICollectionView.reconfigureItems(..)` api and expect the reconfiguration to happen animated as well, you must call this method next to the `UICollectionView` one. `UIKit` in its classic way uses private API to process it. NB: Reconfigure items is not exposed to the layout, it may behave strange and if you experience something like this - move to the `UICollectionView.reloadItems(..)` as a safer option. ### Method `@MainActor open func reconfigureItems(at indexPaths: [IndexPath])` ### Parameters #### Path Parameters - **indexPaths** ([IndexPath]) - Required ``` ```APIDOC ## indexPathForItemPinnedAt(_:) ### Description Returns index path of currently pinned item. ### Method `@MainActor open func indexPathForItemPinnedAt(_ pinningType: ChatItemPinningType) -> IndexPath?` ### Parameters #### Path Parameters - **pinningType** (ChatItemPinningType) - Required ``` ```APIDOC ## indexPathForItemBecomingPinnedAt(_:) ### Description Returns index path of next to become pinned item. ### Method `@MainActor open func indexPathForItemBecomingPinnedAt(_ pinningType: ChatItemPinningType) -> IndexPath?` ### Parameters #### Path Parameters - **pinningType** (ChatItemPinningType) - Required ``` -------------------------------- ### Handle Full Width Layout Case Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Sets the item's width to the full available width and calculates the horizontal offset (dx) to align it with the left inset. ```swift dx = additionalInsets.left itemFrame.size.width = additionalAttributes.layoutFrame.size.width ``` -------------------------------- ### Get All Layout Attributes Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Retrieves all layout attributes for a given state, optionally filtered by a visible rectangle. It initializes `AdditionalLayoutAttributes` and uses a `traverseState` to manage the traversal process. ```swift private func allAttributes(at state: ModelState, visibleRect: CGRect? = nil) -> [ChatLayoutAttributes] { let layout = layout(at: state) let additionalAttributes = AdditionalLayoutAttributes(layoutRepresentation) if let visibleRect { var traverseState: TraverseState = .notFound func check(rect: CGRect) -> Bool { switch traverseState { case .notFound: // ... implementation continues } } // ... implementation continues } // ... implementation continues return [] // Placeholder for actual return } ``` -------------------------------- ### Get Item Attributes for Header Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Retrieves or creates ChatLayoutAttributes for a header item. Handles predefined frames and caching, ensuring correct frame, index path, and zIndex. ```swift func itemAttributes(for itemPath: ItemPath, kind: ItemKind, predefinedFrame: CGRect? = nil, at state: ModelState, additionalAttributes: AdditionalLayoutAttributes? = nil) -> ChatLayoutAttributes? { let additionalAttributes = additionalAttributes ?? AdditionalLayoutAttributes(layoutRepresentation) let attributes: ChatLayoutAttributes let itemIndexPath = itemPath.indexPath let layout = layout(at: state) switch kind { case .header: guard itemPath.section < layout.sections.count, itemPath.item == 0 else { // This occurs when getting layout attributes for initial / final animations return nil } guard let headerFrame = predefinedFrame ?? itemFrame(for: itemPath, kind: kind, at: state, isFinal: true, additionalAttributes: additionalAttributes), let item = item(for: itemPath, kind: kind, at: state) else { return nil } if let cachedAttributes = cachedAttributeObjects[state]?[.header]?[itemPath] { attributes = cachedAttributes } else { attributes = ChatLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: itemIndexPath) cachedAttributeObjects[state]?[.header]?[itemPath] = attributes } #if DEBUG attributes.id = item.id #endif attributes.frame = headerFrame attributes.indexPath = itemIndexPath attributes.zIndex = 10 attributes.alignment = item.alignment } ``` -------------------------------- ### Initialization with Frame Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/MessageContainerView.html Initializes a MessageContainerView with a specified frame rectangle. This is the standard UIView initialization method. ```swift @MainActor public override init(frame: CGRect) ``` -------------------------------- ### Get Visible Bounds Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Calculates and returns the currently visible rectangle within the collection view, considering adjusted content insets. This is useful for determining which items are on screen. ```swift public var visibleBounds: CGRect { guard let collectionView else { return .zero } return CGRect(x: adjustedContentInset.left, y: collectionView.contentOffset.y + adjustedContentInset.top, width: collectionView.bounds.width - adjustedContentInset.left - adjustedContentInset.right, height: collectionView.bounds.height - adjustedContentInset.top - adjustedContentInset.bottom) } ``` -------------------------------- ### Initialize ChatLayout with Horizontal Flip Option Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Default constructor for ChatLayout. Initializes with an option to control horizontal flipping for right-to-left layout support. ```swift public init(flipsHorizontallyInOppositeLayoutDirection: Bool = true) { _flipsHorizontallyInOppositeLayoutDirection = flipsHorizontallyInOppositeLayoutDirection super.init() resetAttributesForPendingAnimations() resetInvalidatedAttributes() } ``` -------------------------------- ### Setup Subviews for RoundedCornersContainerView Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/RoundedCornersContainerView.swift.html Configures the subviews and layout constraints for the container view. This method adds the custom view as a subview and pins its edges to the container's layout margins. ```swift private func setupSubviews() { addSubview(customView) translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false layoutMargins = .zero customView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ customView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor), customView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor), customView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor), customView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor) ]) } ``` -------------------------------- ### ChatLayoutSettings Struct Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Core.html Settings for CollectionViewChatLayout. ```APIDOC ## Struct ChatLayoutSettings ### Description `CollectionViewChatLayout` settings. ### Declaration ```swift public struct ChatLayoutSettings : Equatable, Sendable ``` ``` -------------------------------- ### Get Next Pinned Item Index Path Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Classes/CollectionViewChatLayout.html Returns the index path of the item that is next in line to become pinned. This is helpful for anticipating layout changes related to pinning. ```swift @MainActor open func indexPathForItemBecomingPinnedAt(_ pinningType: ChatItemPinningType) -> IndexPath? ``` -------------------------------- ### Activate Layout Constraints Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/EdgeAligningView.swift.html Activates an array of layout constraints. Ensure all constraints are properly defined before activation. ```swift NSLayoutConstraint.activate(addedConstraints) ``` -------------------------------- ### Get Final Layout Attributes for Disappearing Supplementary View Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Retrieves the final layout attributes for a supplementary view that is about to be removed. Manages attribute copying, offsetting, and alpha/transform for animations. ```swift attributes = controller.itemAttributes(for: elementPath, kind: kind, at: .beforeUpdate)?.typedCopy() ?? ChatLayoutAttributes(forSupplementaryViewOfKind: elementKind, with: elementIndexPath) controller.offsetByTotalCompensation(attributes: attributes, for: state, backward: false) if keepContentOffsetAtBottomOnBatchUpdates, controller.isLayoutBiggerThanVisibleBounds(at: state), let attributes { attributes.frame = attributes.frame.offsetBy(dx: 0, dy: attributes.frame.height * 0.2) } attributes.map { attributes in guard let delegate else { attributes.alpha = 0 return } delegate.finalLayoutAttributesForDeletedItem(self, of: .cell, at: elementIndexPath, modifying: attributes) } ``` ```swift if controller.deletedSectionsIndexes.contains(elementPath.section) ``` ```swift if invalidatedAttributes[kind]?.contains(elementPath) ?? false { attributes = nil } ``` ```swift attributes = controller.itemAttributes(for: finalIndexPath, kind: kind, at: .afterUpdate)?.typedCopy() ``` ```swift attributes = controller.itemAttributes(for: elementPath, kind: kind, at: .beforeUpdate)?.typedCopy() ``` ```swift attributes?.indexPath = elementIndexPath attributesForPendingAnimations[kind]?[elementPath] = attributes if controller.reloadedSectionsIndexes.contains(elementPath.section) { attributes?.alpha = 0 attributes?.transform = CGAffineTransform(scaleX: 0, y: 0) } ``` ```swift attributes = controller.itemAttributes(for: elementPath, kind: kind, at: .beforeUpdate) ``` -------------------------------- ### Initializer with Frame Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/EdgeAligningView.swift.html Initializes an EdgeAligningView with a specified frame. The custom view is created with the same frame. ```swift public override init(frame: CGRect) { customView = CustomView(frame: frame) super.init(frame: frame) setupSubviews() } ``` -------------------------------- ### Get Initial Layout Attributes for Supplementary View Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/CollectionViewChatLayout.swift.html Retrieves layout attributes for a supplementary view before an update. Handles specific cases for iOS 12 to ensure proper positioning. ```swift attributes = controller.itemAttributes(for: initialIndexPath, kind: kind, at: .beforeUpdate)?.typedCopy() ?? ChatLayoutAttributes(forSupplementaryViewOfKind: elementKind, with: elementIndexPath) attributes?.indexPath = elementIndexPath if #unavailable(iOS 13.0) { if controller.reloadedSectionsIndexes.contains(elementPath.section) { // It is needed to position the new cell in the middle of the old cell on ios 12 attributesForPendingAnimations[kind]?[elementPath] = attributes } } ``` ```swift attributes = controller.itemAttributes(for: elementPath, kind: kind, at: .beforeUpdate) ``` -------------------------------- ### Prepare Cell for Reuse Source: https://github.com/ekazaev/chatlayout/blob/master/docs/Protocols/ContainerCollectionViewCellDelegate.html Implement this method to perform any cleanup necessary to prepare the cell for reuse. The default implementation does nothing. ```swift @MainActor func prepareForReuse() ``` -------------------------------- ### Get Chat Layout Attributes with Predicate Source: https://github.com/ekazaev/chatlayout/blob/master/docs/tests/StateController.swift.html Retrieves chat layout attributes intersecting a given rectangle, with optional cache ignoring. Uses binary search for efficient lookup on cached attributes. ```swift let predicate: (ChatLayoutAttributes) -> ComparisonResult = { attributes in if attributes.frame.intersects(rect) { return .orderedSame } else if attributes.frame.minY > rect.maxY { return .orderedDescending } else if attributes.frame.maxY < rect.minY { return .orderedAscending } return .orderedSame } if !ignoreCache, let cachedAttributesState, cachedAttributesState.rect.contains(rect) { return cachedAttributesState.attributes.withUnsafeBufferPointer { $0.binarySearchRange(predicate: predicate) } } else { let totalRect: CGRect switch state { case .beforeUpdate: totalRect = rect.inset(by: UIEdgeInsets(top: -rect.height / 2, left: -rect.width / 2, bottom: -rect.height / 2, right: -rect.width / 2)) case .afterUpdate: totalRect = rect } let attributes = allAttributes(at: state, visibleRect: totalRect) if !ignoreCache { cachedAttributesState = (rect: totalRect, attributes: attributes) } let visibleAttributes = rect != totalRect ? attributes.withUnsafeBufferPointer { $0.binarySearchRange(predicate: predicate) } : attributes return visibleAttributes } ```