Skip to content

Commit

Permalink
Performance improvements during layout calculation (#122)
Browse files Browse the repository at this point in the history
* Performance improvements during layout calculation

Both `invalidateSectionMaxYsCacheForSectionIndices` and `invalidateEntireSectionMaxYsCache` do linear passes. These are called on each section header, section footer and background. Leading to a lot of work being done with long lists.

This PR short circuits the work if we try to remove header, footers and backgrounds that don't exist.

A better option would be to run the calls above once per update vs per update item. That would require a bit more rework though.

* Update MagazineLayout/LayoutCore/ModelState.swift

Co-authored-by: Bryan Keller <[email protected]>

* Update MagazineLayout/LayoutCore/ModelState.swift

Co-authored-by: Bryan Keller <[email protected]>

* Update MagazineLayout/LayoutCore/ModelState.swift

Co-authored-by: Bryan Keller <[email protected]>

---------

Co-authored-by: Elfred Pagan <[email protected]>
Co-authored-by: Bryan Keller <[email protected]>
  • Loading branch information
3 people authored Mar 4, 2024
1 parent bfc6077 commit 2d0d4f9
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 23 deletions.
24 changes: 11 additions & 13 deletions MagazineLayout/LayoutCore/ModelState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -593,11 +593,10 @@ final class ModelState {
}

func removeHeader(forSectionAtIndex sectionIndex: Int) {
currentSectionModels[sectionIndex].removeHeader()

invalidateSectionMaxYsCacheForSectionIndices(startingAt: sectionIndex)

prepareElementLocationsForFlattenedIndices()
if currentSectionModels[sectionIndex].removeHeader() {
invalidateSectionMaxYsCacheForSectionIndices(startingAt: sectionIndex)
prepareElementLocationsForFlattenedIndices()
}
}

func setFooter(_ footerModel: FooterModel, forSectionAtIndex sectionIndex: Int) {
Expand All @@ -609,11 +608,10 @@ final class ModelState {
}

func removeFooter(forSectionAtIndex sectionIndex: Int) {
currentSectionModels[sectionIndex].removeFooter()

invalidateSectionMaxYsCacheForSectionIndices(startingAt: sectionIndex)

prepareElementLocationsForFlattenedIndices()
if currentSectionModels[sectionIndex].removeFooter() {
invalidateSectionMaxYsCacheForSectionIndices(startingAt: sectionIndex)
prepareElementLocationsForFlattenedIndices()
}
}

func setBackground(
Expand All @@ -626,9 +624,9 @@ final class ModelState {
}

func removeBackground(forSectionAtIndex sectionIndex: Int) {
currentSectionModels[sectionIndex].removeBackground()

prepareElementLocationsForFlattenedIndices()
if currentSectionModels[sectionIndex].removeBackground() {
prepareElementLocationsForFlattenedIndices()
}
}

func setSections(_ sectionModels: [SectionModel]) {
Expand Down
26 changes: 16 additions & 10 deletions MagazineLayout/LayoutCore/SectionModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -247,20 +247,22 @@ struct SectionModel {
}
}

mutating func removeHeader() {
if let indexOfHeader = indexOfHeaderRow() {
updateIndexOfFirstInvalidatedRowIfNecessary(toProposedIndex: indexOfHeader)
mutating func removeHeader() -> Bool {
guard let indexOfHeader = indexOfHeaderRow() else {
return false
}

updateIndexOfFirstInvalidatedRowIfNecessary(toProposedIndex: indexOfHeader)
headerModel = nil
return true
}

mutating func removeFooter() {
if let indexOfFooter = indexOfFooterRow() {
updateIndexOfFirstInvalidatedRowIfNecessary(toProposedIndex: indexOfFooter)
mutating func removeFooter() -> Bool {
guard let indexOfFooter = indexOfFooterRow() else {
return false
}

updateIndexOfFirstInvalidatedRowIfNecessary(toProposedIndex: indexOfFooter)
footerModel = nil
return true
}

mutating func updateItemHeight(toPreferredHeight preferredHeight: CGFloat, atIndex index: Int) {
Expand Down Expand Up @@ -334,9 +336,13 @@ struct SectionModel {
// No need to invalidate since the background doesn't affect the layout.
}

mutating func removeBackground() {
backgroundModel = nil
mutating func removeBackground() -> Bool {
guard backgroundModel != nil else {
return false
}
self.backgroundModel = nil
// No need to invalidate since the background doesn't affect the layout.
return true
}

// MARK: Private
Expand Down

0 comments on commit 2d0d4f9

Please sign in to comment.