forked from shadowfacts/Tusker
Add loading indicator to DiffableTimelineLikeTableViewController
This commit is contained in:
parent
8b78a5e7ad
commit
bbfb3b0a7a
|
@ -50,6 +50,7 @@
|
|||
D623A5412635FB3C0095BD04 /* PollOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623A5402635FB3C0095BD04 /* PollOptionView.swift */; };
|
||||
D623A543263634100095BD04 /* PollOptionCheckboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623A542263634100095BD04 /* PollOptionCheckboxView.swift */; };
|
||||
D625E4822588262A0074BB2B /* DraggableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */; };
|
||||
D6262C9A28D01C4B00390C1F /* LoadingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6262C9928D01C4B00390C1F /* LoadingTableViewCell.swift */; };
|
||||
D626493523BD94CE00612E6E /* CompositionAttachmentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493423BD94CE00612E6E /* CompositionAttachmentData.swift */; };
|
||||
D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */; };
|
||||
D626493923C0FD0000612E6E /* AllPhotosTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D626493723C0FD0000612E6E /* AllPhotosTableViewCell.xib */; };
|
||||
|
@ -397,6 +398,7 @@
|
|||
D623A5402635FB3C0095BD04 /* PollOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionView.swift; sourceTree = "<group>"; };
|
||||
D623A542263634100095BD04 /* PollOptionCheckboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionCheckboxView.swift; sourceTree = "<group>"; };
|
||||
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D6262C9928D01C4B00390C1F /* LoadingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D626493423BD94CE00612E6E /* CompositionAttachmentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachmentData.swift; sourceTree = "<group>"; };
|
||||
D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllPhotosTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D626493723C0FD0000612E6E /* AllPhotosTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AllPhotosTableViewCell.xib; sourceTree = "<group>"; };
|
||||
|
@ -1268,6 +1270,7 @@
|
|||
D6DD2A44273D6C5700386A6C /* GIFImageView.swift */,
|
||||
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */,
|
||||
D620483323D3801D008A63EF /* LinkTextView.swift */,
|
||||
D6262C9928D01C4B00390C1F /* LoadingTableViewCell.swift */,
|
||||
D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */,
|
||||
D627943A23A55BA600D38C68 /* NavigableTableViewCell.swift */,
|
||||
D627943123A5466600D38C68 /* SelectableTableViewCell.swift */,
|
||||
|
@ -1918,6 +1921,7 @@
|
|||
D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */,
|
||||
D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */,
|
||||
D6114E1327F89B440080E273 /* TrendingLinkTableViewCell.swift in Sources */,
|
||||
D6262C9A28D01C4B00390C1F /* LoadingTableViewCell.swift in Sources */,
|
||||
D68C2AE325869BAB00548EFF /* AuxiliarySceneDelegate.swift in Sources */,
|
||||
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */,
|
||||
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */,
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
class NotificationsTableViewController: DiffableTimelineLikeTableViewController<NotificationsTableViewController.Section, NotificationGroup> {
|
||||
class NotificationsTableViewController: DiffableTimelineLikeTableViewController<NotificationsTableViewController.Section, NotificationsTableViewController.Item> {
|
||||
|
||||
private let statusCell = "statusCell"
|
||||
private let actionGroupCell = "actionGroupCell"
|
||||
|
@ -56,7 +56,12 @@ class NotificationsTableViewController: DiffableTimelineLikeTableViewController<
|
|||
|
||||
// MARK: - DiffableTimelineLikeTableViewController
|
||||
|
||||
override func cellProvider(_ tableView: UITableView, _ indexPath: IndexPath, _ group: NotificationGroup) -> UITableViewCell? {
|
||||
override func cellProvider(_ tableView: UITableView, _ indexPath: IndexPath, _ item: Item) -> UITableViewCell? {
|
||||
if case .loadingIndicator = item {
|
||||
return self.loadingIndicatorCell(indexPath: indexPath)
|
||||
}
|
||||
let group = item.group!
|
||||
|
||||
switch group.kind {
|
||||
case .mention:
|
||||
guard let notification = group.notifications.first,
|
||||
|
@ -118,7 +123,7 @@ class NotificationsTableViewController: DiffableTimelineLikeTableViewController<
|
|||
self.mastodonController.persistentContainer.addAll(notifications: notifications) {
|
||||
var snapshot = Snapshot()
|
||||
snapshot.appendSections([.notifications])
|
||||
snapshot.appendItems(groups, toSection: .notifications)
|
||||
snapshot.appendItems(groups.map { .notificationGroup($0) }, toSection: .notifications)
|
||||
completion(.success(snapshot))
|
||||
}
|
||||
}
|
||||
|
@ -145,11 +150,11 @@ class NotificationsTableViewController: DiffableTimelineLikeTableViewController<
|
|||
let olderGroups = NotificationGroup.createGroups(notifications: newNotifications, only: self.groupTypes)
|
||||
|
||||
self.mastodonController.persistentContainer.addAll(notifications: newNotifications) {
|
||||
let existingGroups = currentSnapshot().itemIdentifiers
|
||||
let existingGroups = currentSnapshot().itemIdentifiers.compactMap(\.group)
|
||||
let merged = NotificationGroup.mergeGroups(first: existingGroups, second: olderGroups, only: self.groupTypes)
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, NotificationGroup>()
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||
snapshot.appendSections([.notifications])
|
||||
snapshot.appendItems(merged, toSection: .notifications)
|
||||
snapshot.appendItems(merged.map { .notificationGroup($0) }, toSection: .notifications)
|
||||
completion(.success(snapshot))
|
||||
}
|
||||
}
|
||||
|
@ -179,11 +184,11 @@ class NotificationsTableViewController: DiffableTimelineLikeTableViewController<
|
|||
let newerGroups = NotificationGroup.createGroups(notifications: newNotifications, only: self.groupTypes)
|
||||
|
||||
self.mastodonController.persistentContainer.addAll(notifications: newNotifications) {
|
||||
let existingGroups = currentSnapshot().itemIdentifiers
|
||||
let existingGroups = currentSnapshot().itemIdentifiers.compactMap(\.group)
|
||||
let merged = NotificationGroup.mergeGroups(first: newerGroups, second: existingGroups, only: self.groupTypes)
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, NotificationGroup>()
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||
snapshot.appendSections([.notifications])
|
||||
snapshot.appendItems(merged, toSection: .notifications)
|
||||
snapshot.appendItems(merged.map { .notificationGroup($0) }, toSection: .notifications)
|
||||
completion(.success(snapshot))
|
||||
}
|
||||
}
|
||||
|
@ -191,9 +196,12 @@ class NotificationsTableViewController: DiffableTimelineLikeTableViewController<
|
|||
}
|
||||
|
||||
private func dismissNotificationsInGroup(at indexPath: IndexPath, completion: (() -> Void)? = nil) {
|
||||
guard let item = dataSource.itemIdentifier(for: indexPath) else { return }
|
||||
guard let item = dataSource.itemIdentifier(for: indexPath),
|
||||
let notifications = item.group?.notifications else {
|
||||
return
|
||||
}
|
||||
let group = DispatchGroup()
|
||||
item.notifications
|
||||
notifications
|
||||
.map { Pachyderm.Notification.dismiss(id: $0.id) }
|
||||
.forEach { (request) in
|
||||
group.enter()
|
||||
|
@ -241,9 +249,23 @@ class NotificationsTableViewController: DiffableTimelineLikeTableViewController<
|
|||
}
|
||||
|
||||
extension NotificationsTableViewController {
|
||||
enum Section: CaseIterable, Hashable {
|
||||
enum Section: DiffableTimelineLikeSection {
|
||||
case loadingIndicator
|
||||
case notifications
|
||||
}
|
||||
enum Item: DiffableTimelineLikeItem {
|
||||
case loadingIndicator
|
||||
case notificationGroup(NotificationGroup)
|
||||
|
||||
var group: NotificationGroup? {
|
||||
switch self {
|
||||
case .loadingIndicator:
|
||||
return nil
|
||||
case .notificationGroup(let group):
|
||||
return group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NotificationsTableViewController: TuskerNavigationDelegate {
|
||||
|
@ -265,7 +287,7 @@ extension NotificationsTableViewController: StatusTableViewCellDelegate {
|
|||
extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
guard let group = dataSource.itemIdentifier(for: indexPath) else { continue }
|
||||
guard let group = dataSource.itemIdentifier(for: indexPath)?.group else { continue }
|
||||
for notification in group.notifications {
|
||||
guard let avatar = notification.account.avatar else { continue }
|
||||
ImageCache.avatars.fetchIfNotCached(avatar)
|
||||
|
@ -275,7 +297,7 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
|||
|
||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
guard let group = dataSource.itemIdentifier(for: indexPath) else { continue }
|
||||
guard let group = dataSource.itemIdentifier(for: indexPath)?.group else { continue }
|
||||
for notification in group.notifications {
|
||||
guard let avatar = notification.account.avatar else { continue }
|
||||
ImageCache.avatars.cancelWithoutCallback(avatar)
|
||||
|
|
|
@ -60,15 +60,18 @@ class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<Pro
|
|||
// MARK: - DiffableTimelineLikeTableViewController
|
||||
|
||||
override func cellProvider(_ tableView: UITableView, _ indexPath: IndexPath, _ item: Item) -> UITableViewCell? {
|
||||
switch item {
|
||||
case .loadingIndicator:
|
||||
return self.loadingIndicatorCell(indexPath: indexPath)
|
||||
|
||||
case let .status(id: id, state: state, pinned: pinned):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! TimelineStatusTableViewCell
|
||||
|
||||
cell.delegate = self
|
||||
// todo: dataSource.sectionIdentifier is only available on iOS 15
|
||||
cell.showPinned = dataSource.snapshot().indexOfSection(.pinned) == indexPath.section
|
||||
cell.updateUI(statusID: item.id, state: item.state)
|
||||
|
||||
cell.showPinned = pinned
|
||||
cell.updateUI(statusID: id, state: state)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
override func loadInitialItems(completion: @escaping (LoadResult) -> Void) {
|
||||
guard accountID != nil else {
|
||||
|
@ -94,7 +97,7 @@ class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<Pro
|
|||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||
DispatchQueue.main.async {
|
||||
var snapshot = self.dataSource.snapshot()
|
||||
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown, pinned: false) }, toSection: .statuses)
|
||||
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown, pinned: false) }, toSection: .statuses)
|
||||
if self.kind == .statuses {
|
||||
self.loadPinnedStatuses(snapshot: { snapshot }, completion: completion)
|
||||
} else {
|
||||
|
@ -122,7 +125,7 @@ class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<Pro
|
|||
DispatchQueue.main.async {
|
||||
var snapshot = snapshot()
|
||||
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .pinned))
|
||||
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown, pinned: true) }, toSection: .pinned)
|
||||
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown, pinned: true) }, toSection: .pinned)
|
||||
completion(.success(snapshot))
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +154,7 @@ class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<Pro
|
|||
|
||||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||
var snapshot = currentSnapshot()
|
||||
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown, pinned: false) }, toSection: .statuses)
|
||||
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown, pinned: false) }, toSection: .statuses)
|
||||
completion(.success(snapshot))
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +183,7 @@ class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<Pro
|
|||
|
||||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||
var snapshot = currentSnapshot()
|
||||
let items = statuses.map { Item(id: $0.id, state: .unknown, pinned: false) }
|
||||
let items = statuses.map { Item.status(id: $0.id, state: .unknown, pinned: false) }
|
||||
if let first = snapshot.itemIdentifiers(inSection: .statuses).first {
|
||||
snapshot.insertItems(items, beforeItem: first)
|
||||
} else {
|
||||
|
@ -239,22 +242,22 @@ extension ProfileStatusesViewController {
|
|||
}
|
||||
|
||||
extension ProfileStatusesViewController {
|
||||
enum Section: CaseIterable {
|
||||
enum Section: DiffableTimelineLikeSection {
|
||||
case loadingIndicator
|
||||
case pinned
|
||||
case statuses
|
||||
}
|
||||
struct Item: Hashable {
|
||||
let id: String
|
||||
let state: StatusState
|
||||
let pinned: Bool
|
||||
enum Item: DiffableTimelineLikeItem {
|
||||
case loadingIndicator
|
||||
case status(id: String, state: StatusState, pinned: Bool)
|
||||
|
||||
static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||
return lhs.id == rhs.id && lhs.pinned == rhs.pinned
|
||||
var id: String? {
|
||||
switch self {
|
||||
case .loadingIndicator:
|
||||
return nil
|
||||
case .status(id: let id, state: _, pinned: _):
|
||||
return id
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
hasher.combine(pinned)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,9 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
|||
|
||||
override func cellProvider(_ tableView: UITableView, _ indexPath: IndexPath, _ item: Item) -> UITableViewCell? {
|
||||
switch item {
|
||||
case .loadingIndicator:
|
||||
return self.loadingIndicatorCell(indexPath: indexPath)
|
||||
|
||||
case let .status(id: id, state: state):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! TimelineStatusTableViewCell
|
||||
|
||||
|
@ -148,6 +151,9 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
|||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||
DispatchQueue.main.async {
|
||||
var snapshot = self.dataSource.snapshot()
|
||||
if snapshot.sectionIdentifiers.contains(.loadingIndicator) {
|
||||
snapshot.deleteSections([.loadingIndicator])
|
||||
}
|
||||
snapshot.deleteSections([.statuses, .footer])
|
||||
snapshot.appendSections([.statuses, .footer])
|
||||
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown) }, toSection: .statuses)
|
||||
|
@ -245,12 +251,14 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
|||
}
|
||||
|
||||
extension TimelineTableViewController {
|
||||
enum Section: Hashable, CaseIterable {
|
||||
enum Section: DiffableTimelineLikeSection {
|
||||
case loadingIndicator
|
||||
case header
|
||||
case statuses
|
||||
case footer
|
||||
}
|
||||
enum Item: Hashable {
|
||||
enum Item: DiffableTimelineLikeItem {
|
||||
case loadingIndicator
|
||||
case status(id: String, state: StatusState)
|
||||
case confirmLoadMore
|
||||
case publicTimelineDescription(local: Bool)
|
||||
|
@ -270,13 +278,15 @@ extension TimelineTableViewController {
|
|||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
switch self {
|
||||
case let .status(id: id, state: _):
|
||||
case .loadingIndicator:
|
||||
hasher.combine(0)
|
||||
case let .status(id: id, state: _):
|
||||
hasher.combine(1)
|
||||
hasher.combine(id)
|
||||
case .confirmLoadMore:
|
||||
hasher.combine(1)
|
||||
case let .publicTimelineDescription(local: local):
|
||||
hasher.combine(2)
|
||||
case let .publicTimelineDescription(local: local):
|
||||
hasher.combine(3)
|
||||
hasher.combine(local)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,14 @@
|
|||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable, Item: Hashable>: EnhancedTableViewController, RefreshableViewController {
|
||||
protocol DiffableTimelineLikeSection: Hashable, CaseIterable {
|
||||
static var loadingIndicator: Self { get }
|
||||
}
|
||||
protocol DiffableTimelineLikeItem: Hashable {
|
||||
static var loadingIndicator: Self { get }
|
||||
}
|
||||
|
||||
class DiffableTimelineLikeTableViewController<Section: DiffableTimelineLikeSection, Item: DiffableTimelineLikeItem>: EnhancedTableViewController, RefreshableViewController {
|
||||
|
||||
typealias Snapshot = NSDiffableDataSourceSnapshot<Section, Item>
|
||||
typealias LoadResult = Result<Snapshot, LoadError>
|
||||
|
@ -40,6 +47,7 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
|||
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.estimatedRowHeight = 140
|
||||
tableView.register(LoadingTableViewCell.self, forCellReuseIdentifier: "loadingCell")
|
||||
|
||||
#if !targetEnvironment(macCatalyst)
|
||||
self.refreshControl = UIRefreshControl()
|
||||
|
@ -104,15 +112,34 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
|||
dataSource.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
|
||||
private func showLoadingIndicatorDelayed() -> DispatchWorkItem {
|
||||
let workItem = DispatchWorkItem { [weak self] in
|
||||
guard let self = self else { return }
|
||||
var snapshot = self.dataSource.snapshot()
|
||||
snapshot.appendSections([.loadingIndicator])
|
||||
snapshot.appendItems([.loadingIndicator])
|
||||
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(250), execute: workItem)
|
||||
return workItem
|
||||
}
|
||||
|
||||
private func loadInitial() {
|
||||
guard state == .unloaded else { return }
|
||||
// set loaded immediately so we don't trigger another request while the current one is running
|
||||
state = .loadingInitial
|
||||
|
||||
let showIndicator = showLoadingIndicatorDelayed()
|
||||
|
||||
loadInitialItems() { result in
|
||||
DispatchQueue.main.async {
|
||||
showIndicator.cancel()
|
||||
|
||||
switch result {
|
||||
case let .success(snapshot):
|
||||
case .success(var snapshot):
|
||||
if snapshot.sectionIdentifiers.contains(.loadingIndicator) {
|
||||
snapshot.deleteSections([.loadingIndicator])
|
||||
}
|
||||
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||
self.state = .loaded
|
||||
|
||||
|
@ -137,16 +164,22 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
|||
}
|
||||
|
||||
func loadOlder() {
|
||||
guard state != .loadingOlder else { return }
|
||||
guard state == .loaded else { return }
|
||||
|
||||
state = .loadingOlder
|
||||
|
||||
let showIndicator = showLoadingIndicatorDelayed()
|
||||
|
||||
loadOlderItems(currentSnapshot: dataSource.snapshot) { result in
|
||||
DispatchQueue.main.async {
|
||||
self.state = .loaded
|
||||
showIndicator.cancel()
|
||||
|
||||
switch result {
|
||||
case let .success(snapshot):
|
||||
case .success(var snapshot):
|
||||
if snapshot.sectionIdentifiers.contains(.loadingIndicator) {
|
||||
snapshot.deleteSections([.loadingIndicator])
|
||||
}
|
||||
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||
|
||||
case let .failure(.client(error)):
|
||||
|
@ -263,6 +296,12 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
|||
|
||||
// MARK: - Subclass Methods
|
||||
|
||||
func loadingIndicatorCell(indexPath: IndexPath) -> UITableViewCell? {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "loadingCell", for: indexPath) as! LoadingTableViewCell
|
||||
cell.indicator.startAnimating()
|
||||
return cell
|
||||
}
|
||||
|
||||
func cellProvider(_ tableView: UITableView, _ indexPath: IndexPath, _ item: Item) -> UITableViewCell? {
|
||||
fatalError("cellProvider(_:_:_:) must be implemented by subclasses")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// LoadingTableViewCell.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 9/12/22.
|
||||
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class LoadingTableViewCell: UITableViewCell {
|
||||
let indicator = UIActivityIndicatorView(style: .medium)
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
|
||||
indicator.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(indicator)
|
||||
NSLayoutConstraint.activate([
|
||||
indicator.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||
indicator.topAnchor.constraint(equalTo: topAnchor),
|
||||
indicator.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue