feat: add bottomLoader
This commit is contained in:
parent
29439c9746
commit
04d4e7f33a
|
@ -22,6 +22,7 @@
|
|||
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */; };
|
||||
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335D25C1894B00CAE157 /* APIService.swift */; };
|
||||
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */; };
|
||||
2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */; };
|
||||
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */; };
|
||||
2D76316B25C14D4C00929FB9 /* PublicTimelineViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316A25C14D4C00929FB9 /* PublicTimelineViewModel.swift */; };
|
||||
2D76317D25C14DF500929FB9 /* PublicTimelineViewController+StatusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76317C25C14DF400929FB9 /* PublicTimelineViewController+StatusProvider.swift */; };
|
||||
|
@ -33,6 +34,8 @@
|
|||
2D927F0825C7E9A8004F19B8 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D927F0725C7E9A8004F19B8 /* Tag.swift */; };
|
||||
2D927F0E25C7E9C9004F19B8 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D927F0D25C7E9C9004F19B8 /* History.swift */; };
|
||||
2D927F1425C7EDD9004F19B8 /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D927F1325C7EDD9004F19B8 /* Emoji.swift */; };
|
||||
2DA7D04425CA52B200804E11 /* TimelineLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA7D04325CA52B200804E11 /* TimelineLoaderTableViewCell.swift */; };
|
||||
2DA7D04A25CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA7D04925CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift */; };
|
||||
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; };
|
||||
3533495136D843E85211E3E2 /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */; };
|
||||
45B49097460EDE530AD5AA72 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */; };
|
||||
|
@ -159,6 +162,7 @@
|
|||
2D46976325C2A71500CF4AA9 /* UIIamge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIIamge.swift; sourceTree = "<group>"; };
|
||||
2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+Timeline.swift"; sourceTree = "<group>"; };
|
||||
2D61335D25C1894B00CAE157 /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = "<group>"; };
|
||||
2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = "<group>"; };
|
||||
2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewController.swift; sourceTree = "<group>"; };
|
||||
2D76316A25C14D4C00929FB9 /* PublicTimelineViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewModel.swift; sourceTree = "<group>"; };
|
||||
2D76317C25C14DF400929FB9 /* PublicTimelineViewController+StatusProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PublicTimelineViewController+StatusProvider.swift"; sourceTree = "<group>"; };
|
||||
|
@ -170,6 +174,9 @@
|
|||
2D927F0725C7E9A8004F19B8 /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = "<group>"; };
|
||||
2D927F0D25C7E9C9004F19B8 /* History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = History.swift; sourceTree = "<group>"; };
|
||||
2D927F1325C7EDD9004F19B8 /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = "<group>"; };
|
||||
2DA7D04325CA52B200804E11 /* TimelineLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLoaderTableViewCell.swift; sourceTree = "<group>"; };
|
||||
2DA7D04925CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineBottomLoaderTableViewCell.swift; sourceTree = "<group>"; };
|
||||
2DA7D05025CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = "<group>"; };
|
||||
2DF123A625C3B0210020F248 /* ActiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveLabel.swift; sourceTree = "<group>"; };
|
||||
2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -352,6 +359,14 @@
|
|||
path = Persist;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D69CFF225CA9E2200C3A1B2 /* Protocol */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */,
|
||||
);
|
||||
path = Protocol;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D76316325C14BAC00929FB9 /* PublicTimeline */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -406,6 +421,8 @@
|
|||
602D783BEC22881EBAD84419 /* Pods_Mastodon.framework */,
|
||||
A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */,
|
||||
2D7631A725C1535600929FB9 /* TimelinePostTableViewCell.swift */,
|
||||
2DA7D04325CA52B200804E11 /* TimelineLoaderTableViewCell.swift */,
|
||||
2DA7D04925CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift */,
|
||||
);
|
||||
path = TableviewCell;
|
||||
sourceTree = "<group>";
|
||||
|
@ -503,6 +520,7 @@
|
|||
children = (
|
||||
DB427DE325BAA00100D1B89D /* Info.plist */,
|
||||
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */,
|
||||
2D69CFF225CA9E2200C3A1B2 /* Protocol */,
|
||||
2D76319C25C151DE00929FB9 /* Diffiable */,
|
||||
DB8AF52A25C13561002E6C99 /* State */,
|
||||
2D61335525C1886800CAE157 /* Service */,
|
||||
|
@ -676,6 +694,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */,
|
||||
2DA7D05025CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift */,
|
||||
);
|
||||
name = "Recovered References";
|
||||
sourceTree = "<group>";
|
||||
|
@ -1045,6 +1064,7 @@
|
|||
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */,
|
||||
DB45FAE325CA7181005A8AC7 /* MastodonUser.swift in Sources */,
|
||||
DB98338825C945ED00AD9700 /* Assets.swift in Sources */,
|
||||
2DA7D04425CA52B200804E11 /* TimelineLoaderTableViewCell.swift in Sources */,
|
||||
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
|
||||
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */,
|
||||
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */,
|
||||
|
@ -1073,10 +1093,12 @@
|
|||
DB98339C25C96DE600AD9700 /* APIService+Account.swift in Sources */,
|
||||
2D42FF6B25C817D2004A627A /* MastodonContent.swift in Sources */,
|
||||
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */,
|
||||
2DA7D04A25CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift in Sources */,
|
||||
DB8AF55725C137A8002E6C99 /* HomeViewController.swift in Sources */,
|
||||
2D76318325C14E8F00929FB9 /* PublicTimelineViewModel+Diffable.swift in Sources */,
|
||||
2D7631A825C1535600929FB9 /* TimelinePostTableViewCell.swift in Sources */,
|
||||
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */,
|
||||
2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */,
|
||||
DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */,
|
||||
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */,
|
||||
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */,
|
||||
|
|
|
@ -5,16 +5,17 @@
|
|||
// Created by sxiaojian on 2021/1/27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import MastodonSDK
|
||||
import CoreDataStack
|
||||
import Foundation
|
||||
import MastodonSDK
|
||||
|
||||
/// Note: update Equatable when change case
|
||||
enum Item {
|
||||
|
||||
// normal list
|
||||
case toot(objectID: NSManagedObjectID)
|
||||
|
||||
case bottomLoader
|
||||
}
|
||||
|
||||
extension Item: Equatable {
|
||||
|
@ -22,6 +23,8 @@ extension Item: Equatable {
|
|||
switch (lhs, rhs) {
|
||||
case (.toot(let objectIDLeft), .toot(let objectIDRight)):
|
||||
return objectIDLeft == objectIDRight
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +34,8 @@ extension Item: Hashable {
|
|||
switch self {
|
||||
case .toot(let objectID):
|
||||
hasher.combine(objectID)
|
||||
case .bottomLoader:
|
||||
hasher.combine(String(describing: Item.bottomLoader.self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,10 @@ extension TimelineSection {
|
|||
}
|
||||
cell.delegate = timelinePostTableViewCellDelegate
|
||||
return cell
|
||||
case .bottomLoader:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell
|
||||
cell.activityIndicatorView.startAnimating()
|
||||
return cell
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,15 @@ import Foundation
|
|||
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
|
||||
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||
internal enum L10n {
|
||||
|
||||
internal enum Common {
|
||||
internal enum Controls {
|
||||
internal enum Timeline {
|
||||
/// Load More
|
||||
internal static let loadMore = L10n.tr("Localizable", "Common.Controls.Timeline.LoadMore")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
|
||||
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// LoadMoreConfigurableTableViewContainer.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/2/3.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import GameplayKit
|
||||
|
||||
/// The tableView container driven by state machines with "LoadMore" logic
|
||||
protocol LoadMoreConfigurableTableViewContainer: UIViewController {
|
||||
|
||||
associatedtype BottomLoaderTableViewCell: UITableViewCell
|
||||
associatedtype LoadingState: GKState
|
||||
|
||||
var loadMoreConfigurableTableView: UITableView { get }
|
||||
var loadMoreConfigurableStateMachine: GKStateMachine { get }
|
||||
func handleScrollViewDidScroll(_ scrollView: UIScrollView)
|
||||
}
|
||||
|
||||
extension LoadMoreConfigurableTableViewContainer {
|
||||
func handleScrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
guard scrollView === loadMoreConfigurableTableView else { return }
|
||||
|
||||
let cells = loadMoreConfigurableTableView.visibleCells.compactMap { $0 as? BottomLoaderTableViewCell }
|
||||
guard let loaderTableViewCell = cells.first else { return }
|
||||
|
||||
if let tabBar = tabBarController?.tabBar, let window = view.window {
|
||||
let loaderTableViewCellFrameInWindow = loadMoreConfigurableTableView.convert(loaderTableViewCell.frame, to: nil)
|
||||
let windowHeight = window.frame.height
|
||||
let loaderAppear = (loaderTableViewCellFrameInWindow.origin.y + 0.8 * loaderTableViewCell.frame.height) < (windowHeight - tabBar.frame.height)
|
||||
if loaderAppear {
|
||||
loadMoreConfigurableStateMachine.enter(LoadingState.self)
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
} else {
|
||||
loadMoreConfigurableStateMachine.enter(LoadingState.self)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,3 +5,4 @@
|
|||
Created by MainasuK Cirno on 2021/1/22.
|
||||
|
||||
*/
|
||||
"Common.Controls.Timeline.LoadMore" = "Load More";
|
||||
|
|
|
@ -38,6 +38,8 @@ extension PublicTimelineViewController {
|
|||
let toot = managedObjectContext.object(with: objectID) as? Toot
|
||||
promise(.success(toot))
|
||||
}
|
||||
default:
|
||||
promise(.success(nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ final class PublicTimelineViewController: UIViewController, NeedsDependency, Tim
|
|||
lazy var tableView: UITableView = {
|
||||
let tableView = UITableView()
|
||||
tableView.register(TimelinePostTableViewCell.self, forCellReuseIdentifier: String(describing: TimelinePostTableViewCell.self))
|
||||
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.separatorStyle = .none
|
||||
return tableView
|
||||
|
@ -77,23 +78,18 @@ extension PublicTimelineViewController {
|
|||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
viewModel.fetchLatest()
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
os_log("%{public}s[%{public}ld], %{public}s: fetch user timeline latest response error: %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
|
||||
case .finished:
|
||||
break
|
||||
}
|
||||
} receiveValue: { response in
|
||||
let tootsIDs = response.value.map { $0.id }
|
||||
self.viewModel.tootIDs.value = tootsIDs
|
||||
}
|
||||
.store(in: &viewModel.disposeBag)
|
||||
viewModel.stateMachine.enter(PublicTimelineViewModel.State.Loading.self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
extension PublicTimelineViewController {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
handleScrollViewDidScroll(scrollView)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Selector
|
||||
|
||||
extension PublicTimelineViewController {
|
||||
|
@ -130,3 +126,11 @@ extension PublicTimelineViewController: UITableViewDelegate {
|
|||
viewModel.cellFrameCache.setObject(NSValue(cgRect: frame), forKey: NSNumber(value: key))
|
||||
}
|
||||
}
|
||||
// MARK: - LoadMoreConfigurableTableViewContainer
|
||||
extension PublicTimelineViewController: LoadMoreConfigurableTableViewContainer {
|
||||
typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell
|
||||
typealias LoadingState = PublicTimelineViewModel.State.LoadingMore
|
||||
|
||||
var loadMoreConfigurableTableView: UITableView { return tableView }
|
||||
var loadMoreConfigurableStateMachine: GKStateMachine { return viewModel.stateMachine }
|
||||
}
|
||||
|
|
|
@ -121,10 +121,16 @@ extension PublicTimelineViewModel.State {
|
|||
break
|
||||
}
|
||||
} receiveValue: { response in
|
||||
viewModel.isFetchingLatestTimeline.value = false
|
||||
let tootsIDs = response.value.map { $0.id }
|
||||
viewModel.tootIDs.value = tootsIDs
|
||||
stateMachine.enter(Idle.self)
|
||||
var oldTootsIDs = viewModel.tootIDs.value
|
||||
for toot in response.value {
|
||||
if !oldTootsIDs.contains(toot.id) {
|
||||
oldTootsIDs.append(toot.id)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.tootIDs.value = oldTootsIDs
|
||||
|
||||
}
|
||||
.store(in: &viewModel.disposeBag)
|
||||
}
|
||||
|
|
|
@ -73,7 +73,14 @@ class PublicTimelineViewModel: NSObject {
|
|||
var snapshot = NSDiffableDataSourceSnapshot<TimelineSection, Item>()
|
||||
snapshot.appendSections([.main])
|
||||
snapshot.appendItems(items)
|
||||
|
||||
if let currentState = self.stateMachine.currentState {
|
||||
switch currentState {
|
||||
case is State.Idle, is State.LoadingMore, is State.Fail:
|
||||
snapshot.appendItems([.bottomLoader], toSection: .main)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
diffableDataSource.apply(snapshot, animatingDifferences: !items.isEmpty)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// TimelineBottomLoaderTableViewCell.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/2/3.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
final class TimelineBottomLoaderTableViewCell: TimelineLoaderTableViewCell {
|
||||
override func _init() {
|
||||
super._init()
|
||||
|
||||
activityIndicatorView.isHidden = false
|
||||
activityIndicatorView.startAnimating()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// TimelineLoaderTableViewCell.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/2/3.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
class TimelineLoaderTableViewCell: UITableViewCell {
|
||||
|
||||
static let cellHeight: CGFloat = 48
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
let loadMoreButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
|
||||
button.setTitle(L10n.Common.Controls.Timeline.loadMore, for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
let activityIndicatorView: UIActivityIndicatorView = {
|
||||
let activityIndicatorView = UIActivityIndicatorView(style: .medium)
|
||||
activityIndicatorView.tintColor = .white
|
||||
activityIndicatorView.hidesWhenStopped = true
|
||||
return activityIndicatorView
|
||||
}()
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
disposeBag.removeAll()
|
||||
}
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
_init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
_init()
|
||||
}
|
||||
|
||||
func _init() {
|
||||
selectionStyle = .none
|
||||
backgroundColor = Asset.Colors.tootDark.color
|
||||
loadMoreButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(loadMoreButton)
|
||||
NSLayoutConstraint.activate([
|
||||
loadMoreButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
|
||||
loadMoreButton.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
|
||||
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: loadMoreButton.trailingAnchor),
|
||||
contentView.bottomAnchor.constraint(equalTo: loadMoreButton.bottomAnchor, constant: 8),
|
||||
loadMoreButton.heightAnchor.constraint(equalToConstant: TimelineLoaderTableViewCell.cellHeight - 2 * 8).priority(.defaultHigh),
|
||||
])
|
||||
|
||||
activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(activityIndicatorView)
|
||||
NSLayoutConstraint.activate([
|
||||
activityIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||
activityIndicatorView.centerYAnchor.constraint(equalTo: centerYAnchor),
|
||||
])
|
||||
|
||||
loadMoreButton.isHidden = true
|
||||
activityIndicatorView.isHidden = true
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue