From abd5cac189940f5cd795881d13ce0447abfec11e Mon Sep 17 00:00:00 2001 From: Eugene Kazaev Date: Fri, 20 Nov 2020 15:23:22 +0000 Subject: [PATCH] Updated documentation --- docs/tests/ChatLayout.swift.html | 623 +++++++++++++------------- docs/tests/StateController.swift.html | 4 +- docs/tests/index.html | 8 +- 3 files changed, 315 insertions(+), 320 deletions(-) diff --git a/docs/tests/ChatLayout.swift.html b/docs/tests/ChatLayout.swift.html index 101446e3..65714efc 100644 --- a/docs/tests/ChatLayout.swift.html +++ b/docs/tests/ChatLayout.swift.html @@ -9,7 +9,7 @@

Coverage for "ChatLayout.swift" : 0.00%

-

(0 of 715 relevant lines covered)

+

(0 of 714 relevant lines covered)

ChatLayout/Classes/Core/ChatLayout.swift

@@ -3074,1551 +3074,1546 @@

ChatLayout/Classes/Core/ChatLayout.swift

- - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/docs/tests/StateController.swift.html b/docs/tests/StateController.swift.html index dcfda03f..d5ff9758 100644 --- a/docs/tests/StateController.swift.html +++ b/docs/tests/StateController.swift.html @@ -2730,7 +2730,7 @@

ChatLayout/Classes/Core/Model/StateController.swift

- + @@ -2745,7 +2745,7 @@

ChatLayout/Classes/Core/Model/StateController.swift

- + diff --git a/docs/tests/index.html b/docs/tests/index.html index b5103dff..233d5768 100644 --- a/docs/tests/index.html +++ b/docs/tests/index.html @@ -8,7 +8,7 @@
Slather logo

Files for "ChatLayout.xcodeproj"

-Total Coverage : 33.28% +Total Coverage : 33.30%

613
        print("\(#function) \(updateItems)")
!
614
        controller.process(updateItems: updateItems)
!
615614
        state = .afterUpdate
!
616615
        dontReturnAttributes = false
!
617616
        super.prepare(forCollectionViewUpdates: updateItems)
!
618617
    }
!
619618
620619
    /// Performs any additional animations or clean up needed during a collection view update.
621620
    public override func finalizeCollectionViewUpdates() {
!
622621
        controller.proposedCompensatingOffset = 0
!
623622
!
624623
        if keepContentOffsetAtBottomOnBatchUpdates,
!
625624
            controller.isLayoutBiggerThanScreen(at: state),
!
626625
            controller.batchUpdateCompensatingOffset != 0,
!
627626
            let collectionView = collectionView {
!
628627
            let compensatingOffset: CGFloat
!
629628
            if controller.contentSize(for: .beforeUpdate).height > visibleBounds.size.height {
!
630629
                compensatingOffset = controller.batchUpdateCompensatingOffset
!
631630
            } else {
!
632631
                compensatingOffset = maxPossibleContentOffset.y - collectionView.contentOffset.y
!
633632
            }
!
634633
            controller.batchUpdateCompensatingOffset = 0
!
635634
            let context = ChatLayoutInvalidationContext()
!
636635
            context.contentOffsetAdjustment.y = compensatingOffset
!
637636
            context.contentSizeAdjustment.height = controller.contentSize(for: .afterUpdate).height - controller.contentSize(for: .beforeUpdate).height
!
638637
            invalidateLayout(with: context)
!
639638
        } else {
!
640639
            controller.batchUpdateCompensatingOffset = 0
!
641640
            let context = ChatLayoutInvalidationContext()
!
642641
            invalidateLayout(with: context)
!
643642
        }
!
644643
!
645644
        prepareActions.formUnion(.switchStates)
!
646645
!
647646
        super.finalizeCollectionViewUpdates()
!
648647
    }
!
649648
650649
    // MARK: - Cell Appearance Animation
651650
652651
    /// Retrieves the starting layout information for an item being inserted into the collection view.
653652
    public override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
!
654653
        var attributes: ChatLayoutAttributes?
!
655654
!
656655
        if state == .afterUpdate {
!
657656
            if controller.insertedIndexes.contains(itemIndexPath) || controller.insertedSectionsIndexes.contains(itemIndexPath.section) {
!
658657
                attributes = controller.itemAttributes(for: itemIndexPath, kind: .cell, at: .afterUpdate)?.typedCopy()
!
659658
                controller.offsetByTotalCompensation(attributes: attributes, for: state, backward: true)
!
660659
                attributes.map { attributes in
!
661660
                    guard let delegate = delegate else {
!
662661
                        attributes.alpha = 0
!
663662
                        return
!
664663
                    }
!
665664
                    delegate.initialLayoutAttributesForInsertedItem(self, of: .cell, at: itemIndexPath, modifying: attributes, on: .initial)
!
666665
                }
!
667666
                attributesForPendingAnimations[.cell]?[itemIndexPath] = attributes
!
668667
            } else if let itemIdentifier = controller.itemIdentifier(for: itemIndexPath, kind: .cell, at: .afterUpdate),
!
669668
                let initialIndexPath = controller.indexPath(by: itemIdentifier, at: .beforeUpdate) {
!
670669
                attributes = controller.itemAttributes(for: initialIndexPath, kind: .cell, at: .beforeUpdate)?.typedCopy() ?? ChatLayoutAttributes(forCellWith: itemIndexPath)
!
671670
                attributes?.indexPath = itemIndexPath
!
672671
                if #available(iOS 13.0, *) {
!
673672
                } else {
!
674673
                    if controller.reloadedIndexes.contains(itemIndexPath) || controller.reloadedSectionsIndexes.contains(itemIndexPath.section) {
!
675674
                        // It is needed to position the new cell in the middle of the old cell on ios 12
!
676675
                        attributesForPendingAnimations[.cell]?[itemIndexPath] = attributes
!
677676
                    }
!
678677
                }
!
679678
            } else {
!
680679
                attributes = controller.itemAttributes(for: itemIndexPath, kind: .cell, at: .beforeUpdate)
!
681680
            }
!
682681
        } else {
!
683682
            attributes = controller.itemAttributes(for: itemIndexPath, kind: .cell, at: .beforeUpdate)
!
684683
        }
!
685684
!
686685
        return attributes
!
687686
    }
!
688687
689688
    /// Retrieves the final layout information for an item that is about to be removed from the collection view.
690689
    public override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
!
691690
        var attributes: ChatLayoutAttributes?
!
692691
!
693692
        if state == .afterUpdate {
!
694693
            if controller.deletedIndexes.contains(itemIndexPath) || controller.deletedSectionsIndexes.contains(itemIndexPath.section) {
!
695694
                attributes = controller.itemAttributes(for: itemIndexPath, kind: .cell, at: .beforeUpdate)?.typedCopy() ?? ChatLayoutAttributes(forCellWith: itemIndexPath)
!
696695
                controller.offsetByTotalCompensation(attributes: attributes, for: state, backward: false)
!
697696
                if keepContentOffsetAtBottomOnBatchUpdates,
!
698697
                    controller.isLayoutBiggerThanScreen(at: state),
!
699698
                    let attributes = attributes {
!
700699
                    attributes.frame = attributes.frame.offsetBy(dx: 0, dy: attributes.frame.height / 2)
!
701700
                }
!
702701
                attributes.map { attributes in
!
703702
                    guard let delegate = delegate else {
!
704703
                        attributes.alpha = 0
!
705704
                        return
!
706705
                    }
!
707706
                    delegate.finalLayoutAttributesForDeletedItem(self, of: .cell, at: itemIndexPath, modifying: attributes)
!
708707
                }
!
709708
            } else if let itemIdentifier = controller.itemIdentifier(for: itemIndexPath, kind: .cell, at: .beforeUpdate),
!
710709
                let finalIndexPath = controller.indexPath(by: itemIdentifier, at: .afterUpdate) {
!
711710
                if controller.movedIndexes.contains(itemIndexPath) || controller.movedSectionsIndexes.contains(itemIndexPath.section) ||
!
712711
                    controller.reloadedIndexes.contains(itemIndexPath) || controller.reloadedSectionsIndexes.contains(itemIndexPath.section) {
!
713712
                    attributes = controller.itemAttributes(for: finalIndexPath, kind: .cell, at: .afterUpdate)?.typedCopy()
!
714713
                } else {
!
715714
                    attributes = controller.itemAttributes(for: itemIndexPath, kind: .cell, at: .beforeUpdate)?.typedCopy()
!
716715
                }
!
717716
                if invalidatedAttributes[.cell]?.contains(itemIndexPath) ?? false {
!
718717
                    attributes = nil
!
719718
                }
!
720719
!
721720
                attributes?.indexPath = itemIndexPath
!
722721
                attributesForPendingAnimations[.cell]?[itemIndexPath] = attributes
!
723722
                if controller.reloadedIndexes.contains(itemIndexPath) || controller.reloadedSectionsIndexes.contains(itemIndexPath.section) {
!
724723
                    attributes?.alpha = 0
!
725724
                    attributes?.transform = CGAffineTransform(scaleX: 0, y: 0)
!
726725
                }
!
727726
            } else {
!
728727
                attributes = controller.itemAttributes(for: itemIndexPath, kind: .cell, at: .beforeUpdate)
!
729728
            }
!
730729
        } else {
!
731730
            attributes = controller.itemAttributes(for: itemIndexPath, kind: .cell, at: .beforeUpdate)
!
732731
        }
!
733732
!
734733
        return attributes
!
735734
    }
!
736735
737736
    // MARK: - Supplementary View Appearance Animation
738737
739738
    /// Retrieves the starting layout information for a supplementary view being inserted into the collection view.
740739
    public override func initialLayoutAttributesForAppearingSupplementaryElement(ofKind elementKind: String, at elementIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
!
741740
        var attributes: ChatLayoutAttributes?
!
742741
!
743742
        let kind = ItemKind(elementKind)
!
744743
        if state == .afterUpdate {
!
745744
            if controller.insertedSectionsIndexes.contains(elementIndexPath.section) {
!
746745
                attributes = controller.itemAttributes(for: elementIndexPath, kind: kind, at: .afterUpdate)?.typedCopy()
!
747746
                controller.offsetByTotalCompensation(attributes: attributes, for: state, backward: true)
!
748747
                attributes.map { attributes in
!
749748
                    guard let delegate = delegate else {
!
750749
                        attributes.alpha = 0
!
751750
                        return
!
752751
                    }
!
753752
                    delegate.initialLayoutAttributesForInsertedItem(self, of: kind, at: elementIndexPath, modifying: attributes, on: .initial)
!
754753
                }
!
755754
                attributesForPendingAnimations[kind]?[elementIndexPath] = attributes
!
756755
            } else if let itemIdentifier = controller.itemIdentifier(for: elementIndexPath, kind: .cell, at: .afterUpdate),
!
757756
                let initialIndexPath = controller.indexPath(by: itemIdentifier, at: .beforeUpdate) {
!
758757
                attributes = controller.itemAttributes(for: initialIndexPath, kind: kind, at: .beforeUpdate)?.typedCopy() ?? ChatLayoutAttributes(forSupplementaryViewOfKind: elementKind, with: elementIndexPath)
!
759758
                attributes?.indexPath = elementIndexPath
!
760759
!
761760
                if #available(iOS 13.0, *) {
!
762761
                } else {
!
763762
                    if controller.reloadedSectionsIndexes.contains(elementIndexPath.section) {
!
764763
                        // It is needed to position the new cell in the middle of the old cell on ios 12
!
765764
                        attributesForPendingAnimations[.cell]?[initialIndexPath] = attributes
!
766765
                    }
!
767766
                }
!
768767
            } else {
!
769768
                attributes = controller.itemAttributes(for: elementIndexPath, kind: kind, at: .beforeUpdate)
!
770769
            }
!
771770
        } else {
!
772771
            attributes = controller.itemAttributes(for: elementIndexPath, kind: kind, at: .beforeUpdate)
!
773772
        }
!
774773
!
775774
        return attributes
!
776775
    }
!
777776
778777
    /// Retrieves the final layout information for a supplementary view that is about to be removed from the collection view.
779778
    public override func finalLayoutAttributesForDisappearingSupplementaryElement(ofKind elementKind: String, at elementIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
!
780779
        var attributes: ChatLayoutAttributes?
!
781780
!
782781
        let kind = ItemKind(elementKind)
!
783782
        if state == .afterUpdate {
!
784783
            if controller.deletedSectionsIndexes.contains(elementIndexPath.section) {
!
785784
                attributes = controller.itemAttributes(for: elementIndexPath, kind: kind, at: .beforeUpdate)?.typedCopy() ?? ChatLayoutAttributes(forSupplementaryViewOfKind: elementKind, with: elementIndexPath)
!
786785
                controller.offsetByTotalCompensation(attributes: attributes, for: state, backward: false)
!
787786
                if keepContentOffsetAtBottomOnBatchUpdates,
!
788787
                    controller.isLayoutBiggerThanScreen(at: state),
!
789788
                    let attributes = attributes {
!
790789
                    attributes.frame = attributes.frame.offsetBy(dx: 0, dy: attributes.frame.height / 2)
!
791790
                }
!
792791
                attributes.map { attributes in
!
793792
                    guard let delegate = delegate else {
!
794793
                        attributes.alpha = 0
!
795794
                        return
!
796795
                    }
!
797796
                    delegate.finalLayoutAttributesForDeletedItem(self, of: kind, at: elementIndexPath, modifying: attributes)
!
798797
                }
!
799798
            } else if let itemIdentifier = controller.itemIdentifier(for: elementIndexPath, kind: kind, at: .beforeUpdate),
!
800799
                let finalIndexPath = controller.indexPath(by: itemIdentifier, at: .afterUpdate) {
!
801800
                attributes = controller.itemAttributes(for: finalIndexPath, kind: kind, at: .afterUpdate)?.typedCopy() ?? ChatLayoutAttributes(forSupplementaryViewOfKind: elementKind, with: elementIndexPath)
!
802801
                attributes?.indexPath = elementIndexPath
!
803802
                attributesForPendingAnimations[kind]?[elementIndexPath] = attributes
!
804803
                if controller.reloadedSectionsIndexes.contains(elementIndexPath.section) || finalIndexPath != elementIndexPath {
!
805804
                    attributes?.alpha = 0
!
806805
                    attributes?.transform = CGAffineTransform(scaleX: 0, y: 0)
!
807806
                }
!
808807
            } else {
!
809808
                attributes = controller.itemAttributes(for: elementIndexPath, kind: kind, at: .beforeUpdate)
!
810809
            }
!
811810
        } else {
!
812811
            attributes = controller.itemAttributes(for: elementIndexPath, kind: kind, at: .beforeUpdate)
!
813812
        }
!
814813
        return attributes
!
815814
    }
!
816815
817816
}
818817
819818
extension ChatLayout {
820819
821820
    func configuration(for element: ItemKind, at indexPath: IndexPath) -> ItemModel.Configuration {
!
822821
        let itemSize = estimatedSize(for: element, at: indexPath)
!
823822
        return ItemModel.Configuration(alignment: alignment(for: element, at: indexPath), preferredSize: itemSize.estimated, calculatedSize: itemSize.exact)
!
824823
    }
!
825824
826825
    private func estimatedSize(for element: ItemKind, at indexPath: IndexPath) -> (estimated: CGSize, exact: CGSize?) {
!
827826
        guard let delegate = delegate else {
!
828827
            return (estimated: estimatedItemSize, exact: nil)
!
829828
        }
!
830829
!
831830
        let itemSize = delegate.sizeForItem(self, of: element, at: indexPath)
!
832831
!
833832
        switch itemSize {
!
834833
        case .auto:
!
835834
            return (estimated: estimatedItemSize, exact: nil)
!
836835
        case let .estimated(size):
!
837836
            return (estimated: size, exact: nil)
!
838837
        case let .exact(size):
!
839838
            return (estimated: size, exact: size)
!
840839
        }
!
841840
    }
!
842841
843842
    fileprivate func itemSize(with preferredAttributes: ChatLayoutAttributes) -> CGSize {
!
844843
        let itemSize: CGSize
!
845844
        if let delegate = delegate,
!
846845
            case let .exact(size) = delegate.sizeForItem(self, of: preferredAttributes.kind, at: preferredAttributes.indexPath) {
!
847846
            itemSize = size
!
848847
        } else {
!
849848
            itemSize = preferredAttributes.size
!
850849
        }
!
851850
        return itemSize
!
852851
    }
!
853852
854853
    private func alignment(for element: ItemKind, at indexPath: IndexPath) -> ChatItemAlignment {
!
855854
        guard let delegate = delegate else {
!
856855
            return .full
!
857856
        }
!
858857
        return delegate.alignmentForItem(self, of: element, at: indexPath)
!
859858
    }
!
860859
861860
    private var estimatedItemSize: CGSize {
!
862861
        guard let estimatedItemSize = settings.estimatedItemSize else {
!
863862
            guard collectionView != nil else {
!
864863
                return .zero
!
865864
            }
!
866865
            return CGSize(width: layoutFrame.width, height: 40)
!
867866
        }
!
868867
!
869868
        return estimatedItemSize
!
870869
    }
!
871870
872871
    private func resetAttributesForPendingAnimations() {
!
873872
        ItemKind.allCases.forEach {
!
874873
            attributesForPendingAnimations[$0] = [:]
!
875874
        }
!
876875
    }
!
877876
878877
    private func resetInvalidatedAttributes() {
!
879878
        ItemKind.allCases.forEach {
!
880879
            invalidatedAttributes[$0] = []
!
881880
        }
!
882881
    }
!
883882
884883
}
885884
886885
extension ChatLayout: ChatLayoutRepresentation {
887886
888887
    func numberOfItems(inSection section: Int) -> Int {
!
889888
        guard let collectionView = collectionView else {
!
890889
            return .zero
!
891890
        }
!
892891
        return collectionView.numberOfItems(inSection: section)
!
893892
    }
!
894893
895894
    func shouldPresentHeader(at sectionIndex: Int) -> Bool {
!
896895
        return delegate?.shouldPresentHeader(self, at: sectionIndex) ?? false
!
897896
    }
!
898897
899898
    func shouldPresentFooter(at sectionIndex: Int) -> Bool {
!
900899
        return delegate?.shouldPresentFooter(self, at: sectionIndex) ?? false
!
901900
    }
!
902901
903902
}
904903
905904
extension ChatLayout {
906905
907906
    private var maxPossibleContentOffset: CGPoint {
!
908907
        guard let collectionView = collectionView else {
!
909908
            return .zero
!
910909
        }
!
911910
        let maxContentOffset = max(0 - collectionView.adjustedContentInset.top, controller.contentHeight(at: state) - collectionView.frame.height + collectionView.adjustedContentInset.bottom)
!
912911
        return CGPoint(x: 0, y: maxContentOffset)
!
913912
    }
!
914913
915914
    private var isUserInitiatedScrolling: Bool {
!
916915
        guard let collectionView = collectionView else {
!
917916
            return false
!
918917
        }
!
919918
        return collectionView.isDragging || collectionView.isDecelerating
!
920919
    }
!
921920
922921
}
544
        insertedIndexes.sorted(by: { $0 < $1 }).forEach {
1840000x1850000x
545 547
        deletedIndexes.sorted(by: { $0 < $1 }).forEach {
1840000x1860000x
548
@@ -23,10 +23,10 @@

- - + + - +
0.00 ChatLayout.swift922715921714 0715714
20.41