-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/partial reloading #49
base: develop
Are you sure you want to change the base?
Changes from all commits
117fddc
5265b99
8193f7a
042ca6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// | ||
// TableViewController+Reload.swift | ||
// ThunderTable | ||
// | ||
// Created by Simon Mitchell on 17/07/2018. | ||
// Copyright © 2018 3SidedCube. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
|
||
extension TableViewController { | ||
|
||
/// Finds the index path for a particular row. | ||
/// | ||
/// - Note: The row in question must conform to Equatable for this to succeed. | ||
/// | ||
/// - Parameter row: The row to find the index path for. | ||
/// - Returns: The index path the row is positioned at if it is in `data`. | ||
public func indexPathFor<T: Row & Equatable>(row: T) -> IndexPath? { | ||
|
||
for (index, section) in data.enumerated() { | ||
|
||
guard let matchingRowIndex = section.rows.firstIndex(where: { | ||
guard let matchableRow = $0 as? T else { return false } | ||
return matchableRow == row | ||
}) else { | ||
continue | ||
} | ||
|
||
return IndexPath(row: matchingRowIndex, section: index) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
/// Replaces a row in `data` with another row, whilst optionally reloading other index paths. | ||
/// | ||
/// - Note: This relies on the containing section of the row being of class `TableSection` as apposed to any object conforming to `Section` | ||
/// | ||
/// - Parameters: | ||
/// - row: The row that should be replaced. | ||
/// - otherRow: The row that is replacing the original row. | ||
/// - additionalReloadIndexPaths: Additional index paths that should be reloaded at the same time. | ||
/// - animation: The animation to use when reloading the rows | ||
public func replace<T: Row & Equatable>(row: T, with otherRow: Row, reloading additionalReloadIndexPaths: [IndexPath] = [], animation: UITableView.RowAnimation = .none) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice to reuse the array option below if we can, save code duplication - happy to leave ofc |
||
|
||
guard let indexPath = indexPathFor(row: row) else { return } | ||
guard let tableSection = data[indexPath.section] as? TableSection else { return } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
var rows = tableSection.rows | ||
rows[indexPath.row] = otherRow | ||
tableSection.rows = rows | ||
|
||
withoutRedrawing { | ||
data[indexPath.section] = tableSection | ||
} | ||
|
||
var indexPaths = [indexPath] | ||
indexPaths.append(contentsOf: additionalReloadIndexPaths) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Happy to leave, but user's could wrap |
||
tableView.reloadRows(at: indexPaths, with: animation) | ||
} | ||
|
||
/// Replaces multiple rows with replacement rows. | ||
/// | ||
/// - Note: This relies on the containing section of each row being of class `TableSection` as apposed to any object conforming to `Section`. | ||
/// | ||
/// - Parameters: | ||
/// - rows: The rows to replace. | ||
/// - otherRows: The rows they should be replaced by. | ||
/// - animation: The row animation to use when replacing | ||
public func replace<T: Row & Equatable>(rows: [T], with otherRows: [Row], animation: UITableView.RowAnimation) { | ||
|
||
guard rows.count == otherRows.count else { return } | ||
|
||
let replacement: [(Int, IndexPath)] = rows.enumerated().compactMap({ | ||
guard let indexPath = indexPathFor(row: $0.element) else { return nil } | ||
return ($0.offset, indexPath) | ||
}) | ||
|
||
replacement.forEach { (index, indexPath) in | ||
|
||
guard let tableSection = data[indexPath.section] as? TableSection else { return } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again do we need |
||
|
||
var rows = tableSection.rows | ||
rows[indexPath.row] = otherRows[index] | ||
tableSection.rows = rows | ||
|
||
withoutRedrawing { | ||
data[indexPath.section] = tableSection | ||
} | ||
} | ||
|
||
tableView.reloadRows(at: replacement.map({ $0.1 }), with: animation) | ||
} | ||
|
||
/// Reloads the cell at the indexPath for a given row. | ||
/// | ||
/// - Parameters: | ||
/// - row: The row to reload the cell for. | ||
/// - animation: The animation to use when redrawing | ||
public func redraw<T: Row & Equatable>(row: T, with animation: UITableView.RowAnimation = .none) { | ||
|
||
guard let indexPath = indexPathFor(row: row) else { return } | ||
tableView.reloadRows(at: [indexPath], with: animation) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we are using animation shouldn't we wrap in a |
||
} | ||
|
||
/// The last available indexPath in the tableView | ||
public var lastIndexPath: IndexPath? { | ||
guard let lastSection = data.last(where: { !$0.rows.isEmpty }) else { return nil } | ||
return IndexPath(row: lastSection.rows.count - 1, section: data.count - 1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ooooh yikes yep this needs more consideration! |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -111,9 +111,21 @@ open class TableViewController: UITableViewController, UIContentSizeCategoryAdju | |
|
||
private var _data: [Section] = [] | ||
|
||
private var _isBlockingRedrawing: Bool = false | ||
|
||
/// A function which allows for mutation of `data` without causing the tableView to reload. | ||
/// | ||
/// - Parameter closure: Code to be run without reloading the table view. | ||
public func withoutRedrawing(_ closure: () -> Void) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
_isBlockingRedrawing = true | ||
closure() | ||
_isBlockingRedrawing = false | ||
} | ||
|
||
open var data: [Section] { | ||
set { | ||
_data = newValue | ||
guard !_isBlockingRedrawing else { return } | ||
tableView.reloadData() | ||
} | ||
get { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could do fancy setters on
subscripts
here! But let's leave that for now 😄. Bit OTT