Fixing reuse and sizing issues.

This commit is contained in:
Pfeil, Scott Robert 2020-04-10 11:15:55 -04:00
parent 185efa547e
commit 8da03356e4
4 changed files with 63 additions and 34 deletions

View File

@ -54,6 +54,32 @@ open class Carousel: View {
private var size: CGFloat? private var size: CGFloat?
// Updates the model and index.
public func updateModelIndex() {
(model as? CarouselModel)?.index = pageIndex
}
open override func layoutSubviews() {
super.layoutSubviews()
// Accounts for any collection size changes
DispatchQueue.main.async {
self.layoutCollection()
}
}
/// Invalidates the layout and ensures we are paged to the correct cell.
open func layoutCollection() {
collectionView.collectionViewLayout.invalidateLayout()
showPeaking(false)
// Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled.
DispatchQueue.main.async {
self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false)
self.collectionView.layoutIfNeeded()
self.showPeaking(true)
}
}
// MARK: - MVMCoreViewProtocol // MARK: - MVMCoreViewProtocol
open override func setupView() { open override func setupView() {
super.setupView() super.setupView()
@ -73,15 +99,12 @@ open class Carousel: View {
open override func updateView(_ size: CGFloat) { open override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
self.size = size self.size = size
collectionView.collectionViewLayout.invalidateLayout()
showPeaking(false) // Update cells and re-layout.
for cell in collectionView.visibleCells {
// Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled. (cell as? MVMCoreViewProtocol)?.updateView(size)
DispatchQueue.main.async {
self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false)
self.collectionView.layoutIfNeeded()
self.showPeaking(true)
} }
layoutCollection()
} }
// MARK: - MoleculeViewProtocol // MARK: - MoleculeViewProtocol
@ -108,6 +131,9 @@ open class Carousel: View {
} }
setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject) setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject)
pageIndex = carouselModel.index
pagingView?.setPage(carouselModel.index)
collectionView.reloadData() collectionView.reloadData()
} }
@ -197,6 +223,7 @@ open class Carousel: View {
} }
let currentPage = pager.currentPage() let currentPage = pager.currentPage()
localSelf.pageIndex = currentPage localSelf.pageIndex = currentPage
localSelf.updateModelIndex()
localSelf.goTo(localSelf.currentIndex, animated: !UIAccessibility.isVoiceOverRunning) localSelf.goTo(localSelf.currentIndex, animated: !UIAccessibility.isVoiceOverRunning)
}) })
} }
@ -246,7 +273,7 @@ open class Carousel: View {
extension Carousel: UICollectionViewDelegateFlowLayout { extension Carousel: UICollectionViewDelegateFlowLayout {
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemWidth = (size ?? collectionView.bounds.width) * CGFloat(itemWidthPercent) let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
return CGSize(width: itemWidth, height: collectionView.bounds.height) return CGSize(width: itemWidth, height: collectionView.bounds.height)
} }
@ -280,19 +307,18 @@ extension Carousel: UIScrollViewDelegate {
func goTo(_ index: Int, animated: Bool) { func goTo(_ index: Int, animated: Bool) {
showPeaking(false) showPeaking(false)
setAccessiblity(collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)), index: index) setAccessiblity(collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0)), index: index)
self.currentIndex = index currentIndex = index
self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: animated) updateModelIndex()
if let cell = collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)) { collectionView.scrollToItem(at: IndexPath(row: currentIndex, section: 0), at: itemAlignment, animated: animated)
setAccessiblity(collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)), index: index) if let cell = collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0)) {
setAccessiblity(collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0)), index: index)
UIAccessibility.post(notification: .layoutChanged, argument: cell) UIAccessibility.post(notification: .layoutChanged, argument: cell)
} }
} }
func handleUserOnBufferCell() { func handleUserOnBufferCell() {
guard loop else { guard loop else { return }
return
}
let lastPageIndex = numberOfPages + 1 let lastPageIndex = numberOfPages + 1
let goToIndex = {(index: Int) in let goToIndex = {(index: Int) in
@ -320,9 +346,11 @@ extension Carousel: UIScrollViewDelegate {
let index = scrollView.contentOffset.x / (itemWidth + separatorWidth) let index = scrollView.contentOffset.x / (itemWidth + separatorWidth)
let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1 let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1
if index < 1 { if index < 1 {
self.currentIndex = 0 currentIndex = 0
updateModelIndex()
} else if index > CGFloat(lastCellIndex - 1) { } else if index > CGFloat(lastCellIndex - 1) {
self.currentIndex = lastCellIndex currentIndex = lastCellIndex
updateModelIndex()
} }
} }
@ -348,9 +376,7 @@ extension Carousel: UIScrollViewDelegate {
targetContentOffset.pointee = scrollView.contentOffset targetContentOffset.pointee = scrollView.contentOffset
// This is for setting up smooth custom paging. (Since UICollectionView only handles paging based on collection view size and not cell size). // This is for setting up smooth custom paging. (Since UICollectionView only handles paging based on collection view size and not cell size).
guard let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing else { guard let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing else { return }
return
}
// We switch cards if we pass the velocity threshold or position threshold (currently 50%). // We switch cards if we pass the velocity threshold or position threshold (currently 50%).
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent) let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)

View File

@ -12,7 +12,7 @@ import UIKit
public static var identifier: String = "carousel" public static var identifier: String = "carousel"
public var backgroundColor: Color? public var backgroundColor: Color?
public var molecules: [CarouselItemModel] public var molecules: [CarouselItemModel]
public var index: Int = 0
public var spacing: Float? public var spacing: Float?
public var border: Bool? public var border: Bool?
public var loop: Bool? public var loop: Bool?
@ -29,6 +29,7 @@ import UIKit
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case molecules case molecules
case index
case spacing case spacing
case border case border
case loop case loop
@ -40,15 +41,16 @@ import UIKit
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
self.molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules) molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules)
self.backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) index = try typeContainer.decodeIfPresent(Int.self, forKey: .index) ?? 0
self.spacing = try typeContainer.decodeIfPresent(Float.self, forKey: .spacing) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
self.border = try typeContainer.decodeIfPresent(Bool.self, forKey: .border) spacing = try typeContainer.decodeIfPresent(Float.self, forKey: .spacing)
self.loop = try typeContainer.decodeIfPresent(Bool.self, forKey: .loop) border = try typeContainer.decodeIfPresent(Bool.self, forKey: .border)
self.height = try typeContainer.decodeIfPresent(Float.self, forKey: .height) loop = try typeContainer.decodeIfPresent(Bool.self, forKey: .loop)
self.itemWidthPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .itemWidthPercent) height = try typeContainer.decodeIfPresent(Float.self, forKey: .height)
self.itemAlignment = try typeContainer.decodeIfPresent(UICollectionView.ScrollPosition.self, forKey: .itemAlignment) itemWidthPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .itemWidthPercent)
self.pagingMolecule = try typeContainer.decodeModelIfPresent(codingKey: .pagingMolecule) itemAlignment = try typeContainer.decodeIfPresent(UICollectionView.ScrollPosition.self, forKey: .itemAlignment)
pagingMolecule = try typeContainer.decodeModelIfPresent(codingKey: .pagingMolecule)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {

View File

@ -33,7 +33,7 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
bottomView.updateView(width) bottomView.updateView(width)
showFooter(width) showFooter(width)
} }
self.tableView?.reloadData() tableView?.reloadData()
} }
open override func handleNewData() { open override func handleNewData() {

View File

@ -278,7 +278,8 @@ import UIKit
return return
} }
if needsUpdateUI || screenSizeChanged() { // First update should be explicit (hence the zero check)
if needsUpdateUI || (previousScreenSize != .zero && screenSizeChanged()) {
updateViews() updateViews()
needsUpdateUI = false needsUpdateUI = false
} }