Compare commits
6 Commits
4fdafa893e
...
f4f2a5546c
Author | SHA1 | Date |
---|---|---|
Shadowfacts | f4f2a5546c | |
Shadowfacts | b220948e2b | |
Shadowfacts | 866edc472d | |
Shadowfacts | 88e4f52b5d | |
Shadowfacts | 98529ca5af | |
Shadowfacts | 6d8c5f632c |
|
@ -158,6 +158,7 @@
|
||||||
D6757A7E2157E02600721E32 /* XCBRequestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A7D2157E02600721E32 /* XCBRequestSpec.swift */; };
|
D6757A7E2157E02600721E32 /* XCBRequestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A7D2157E02600721E32 /* XCBRequestSpec.swift */; };
|
||||||
D6757A822157E8FA00721E32 /* XCBSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A812157E8FA00721E32 /* XCBSession.swift */; };
|
D6757A822157E8FA00721E32 /* XCBSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6757A812157E8FA00721E32 /* XCBSession.swift */; };
|
||||||
D67895BC24671E6D00D4CD9E /* PKDrawing+Render.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67895BB24671E6D00D4CD9E /* PKDrawing+Render.swift */; };
|
D67895BC24671E6D00D4CD9E /* PKDrawing+Render.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67895BB24671E6D00D4CD9E /* PKDrawing+Render.swift */; };
|
||||||
|
D67895C0246870DE00D4CD9E /* LocalAccountAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */; };
|
||||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D679C09E215850EF00DA27FE /* XCBActions.swift */; };
|
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D679C09E215850EF00DA27FE /* XCBActions.swift */; };
|
||||||
D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */; };
|
D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */; };
|
||||||
D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */; };
|
D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */; };
|
||||||
|
@ -454,6 +455,7 @@
|
||||||
D6757A7D2157E02600721E32 /* XCBRequestSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBRequestSpec.swift; sourceTree = "<group>"; };
|
D6757A7D2157E02600721E32 /* XCBRequestSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBRequestSpec.swift; sourceTree = "<group>"; };
|
||||||
D6757A812157E8FA00721E32 /* XCBSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBSession.swift; sourceTree = "<group>"; };
|
D6757A812157E8FA00721E32 /* XCBSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBSession.swift; sourceTree = "<group>"; };
|
||||||
D67895BB24671E6D00D4CD9E /* PKDrawing+Render.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PKDrawing+Render.swift"; sourceTree = "<group>"; };
|
D67895BB24671E6D00D4CD9E /* PKDrawing+Render.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PKDrawing+Render.swift"; sourceTree = "<group>"; };
|
||||||
|
D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAccountAvatarView.swift; sourceTree = "<group>"; };
|
||||||
D679C09E215850EF00DA27FE /* XCBActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActions.swift; sourceTree = "<group>"; };
|
D679C09E215850EF00DA27FE /* XCBActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActions.swift; sourceTree = "<group>"; };
|
||||||
D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeAccountDetailView.swift; sourceTree = "<group>"; };
|
D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeAccountDetailView.swift; sourceTree = "<group>"; };
|
||||||
D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Uniques.swift"; sourceTree = "<group>"; };
|
D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Uniques.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -942,6 +944,7 @@
|
||||||
D6BC9DB2232D4C07002CA326 /* WellnessPrefsView.swift */,
|
D6BC9DB2232D4C07002CA326 /* WellnessPrefsView.swift */,
|
||||||
0427033922B31269000D31B6 /* AdvancedPrefsView.swift */,
|
0427033922B31269000D31B6 /* AdvancedPrefsView.swift */,
|
||||||
0427037B22B316B9000D31B6 /* SilentActionPrefs.swift */,
|
0427037B22B316B9000D31B6 /* SilentActionPrefs.swift */,
|
||||||
|
D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */,
|
||||||
);
|
);
|
||||||
path = Preferences;
|
path = Preferences;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1743,6 +1746,7 @@
|
||||||
D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */,
|
D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */,
|
||||||
D627943523A5525100D38C68 /* StatusActivity.swift in Sources */,
|
D627943523A5525100D38C68 /* StatusActivity.swift in Sources */,
|
||||||
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
|
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
|
||||||
|
D67895C0246870DE00D4CD9E /* LocalAccountAvatarView.swift in Sources */,
|
||||||
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */,
|
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */,
|
||||||
D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */,
|
D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */,
|
||||||
D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */,
|
D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */,
|
||||||
|
|
|
@ -13,15 +13,18 @@ import Combine
|
||||||
|
|
||||||
class MastodonCachePersistentStore: NSPersistentContainer {
|
class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
|
|
||||||
|
private static let managedObjectModel: NSManagedObjectModel = {
|
||||||
|
let url = Bundle.main.url(forResource: "Tusker", withExtension: "momd")!
|
||||||
|
return NSManagedObjectModel(contentsOf: url)!
|
||||||
|
}()
|
||||||
|
|
||||||
private(set) lazy var backgroundContext = newBackgroundContext()
|
private(set) lazy var backgroundContext = newBackgroundContext()
|
||||||
|
|
||||||
let statusSubject = PassthroughSubject<String, Never>()
|
let statusSubject = PassthroughSubject<String, Never>()
|
||||||
let accountSubject = PassthroughSubject<String, Never>()
|
let accountSubject = PassthroughSubject<String, Never>()
|
||||||
|
|
||||||
init(for controller: MastodonController) {
|
init(for controller: MastodonController) {
|
||||||
let url = Bundle.main.url(forResource: "Tusker", withExtension: "momd")!
|
super.init(name: "\(controller.accountInfo!.id)_cache", managedObjectModel: MastodonCachePersistentStore.managedObjectModel)
|
||||||
let model = NSManagedObjectModel(contentsOf: url)!
|
|
||||||
super.init(name: "\(controller.accountInfo!.id)_cache", managedObjectModel: model)
|
|
||||||
loadPersistentStores { (description, error) in
|
loadPersistentStores { (description, error) in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
fatalError("Unable to load persistent store: \(error)")
|
fatalError("Unable to load persistent store: \(error)")
|
||||||
|
|
|
@ -127,6 +127,33 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
// MARK: - Table view delegate
|
// MARK: - Table view delegate
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
|
// see TimelineTableViewController.tableView(_:willDisplay:forRowAt:)
|
||||||
|
if !isCurrentlyScrollingToTop, scrollViewDirection < 0 {
|
||||||
|
let pageSize = 20
|
||||||
|
if groups.count > 2 * pageSize,
|
||||||
|
indexPath.row < groups.count - (2 * pageSize) {
|
||||||
|
let groupsToRemove = groups[groups.count - pageSize..<groups.count]
|
||||||
|
for group in groupsToRemove {
|
||||||
|
for notification in group.notifications {
|
||||||
|
// todo: reference count accounts
|
||||||
|
// mastodonController.persistentContainer.account(for: notification.account.id)?.decrementReferenceCount()
|
||||||
|
if let id = notification.status?.id {
|
||||||
|
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let removedIndexPaths = (groups.count - 20..<groups.count).map { IndexPath(row: $0, section: 0) }
|
||||||
|
|
||||||
|
groups.removeLast(pageSize)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
tableView.deleteRows(at: removedIndexPaths, with: .none)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if indexPath.row == groups.count - 1 {
|
if indexPath.row == groups.count - 1 {
|
||||||
guard let older = older else { return }
|
guard let older = older else { return }
|
||||||
|
|
||||||
|
|
|
@ -68,13 +68,16 @@ extension OnboardingViewController: InstanceSelectorTableViewControllerDelegate
|
||||||
let authCode = item.value else { return }
|
let authCode = item.value else { return }
|
||||||
|
|
||||||
mastodonController.authorize(authorizationCode: authCode) { (accessToken) in
|
mastodonController.authorize(authorizationCode: authCode) { (accessToken) in
|
||||||
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: nil, accessToken: accessToken)
|
// construct a temporary UserAccountInfo instance for the MastodonController to use to fetch it's own account
|
||||||
mastodonController.accountInfo = accountInfo
|
let tempAccountInfo = LocalData.UserAccountInfo(id: "temp", instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: nil, accessToken: accessToken)
|
||||||
|
mastodonController.accountInfo = tempAccountInfo
|
||||||
|
|
||||||
mastodonController.getOwnAccount { (account) in
|
mastodonController.getOwnAccount { (account) in
|
||||||
LocalData.shared.setUsername(for: accountInfo, username: account.username)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
// this needs to happen on the main thread because it publishes a new value for the ObservableObject
|
||||||
|
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: account.username, accessToken: accessToken)
|
||||||
|
mastodonController.accountInfo = accountInfo
|
||||||
|
|
||||||
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
|
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// LocalAccountAvatarView.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 5/10/20.
|
||||||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct LocalAccountAvatarView: View {
|
||||||
|
let localAccountInfo: LocalData.UserAccountInfo
|
||||||
|
@State
|
||||||
|
var avatarImage: UIImage? = nil
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let image: Image
|
||||||
|
if avatarImage == nil {
|
||||||
|
image = Image(systemName: "person.crop.square")
|
||||||
|
} else {
|
||||||
|
image = Image(uiImage: self.avatarImage!).renderingMode(.original)
|
||||||
|
}
|
||||||
|
return image
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 30, height: 30)
|
||||||
|
.onAppear(perform: self.loadImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadImage() {
|
||||||
|
let controller = MastodonController.getForAccount(localAccountInfo)
|
||||||
|
controller.getOwnAccount { (account) in
|
||||||
|
_ = ImageCache.avatars.get(account.avatar) { (data) in
|
||||||
|
if let data = data, let image = UIImage(data: data) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.avatarImage = image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//struct LocalAccountAvatarView_Previews: PreviewProvider {
|
||||||
|
// static var previews: some View {
|
||||||
|
// LocalAccountAvatarView()
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -21,8 +21,14 @@ struct PreferencesView: View {
|
||||||
NotificationCenter.default.post(name: .activateAccount, object: nil, userInfo: ["account": account])
|
NotificationCenter.default.post(name: .activateAccount, object: nil, userInfo: ["account": account])
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Text(account.username)
|
LocalAccountAvatarView(localAccountInfo: account)
|
||||||
.foregroundColor(.primary)
|
VStack(alignment: .leading) {
|
||||||
|
Text(verbatim: account.username)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
Text(verbatim: account.instanceURL.host!)
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
if account == self.localData.getMostRecentAccount() {
|
if account == self.localData.getMostRecentAccount() {
|
||||||
Image(systemName: "checkmark")
|
Image(systemName: "checkmark")
|
||||||
|
|
|
@ -82,10 +82,11 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
||||||
let request = actionType == .favorite ? Status.getFavourites(status.id) : Status.getReblogs(status.id)
|
let request = actionType == .favorite ? Status.getFavourites(status.id) : Status.getReblogs(status.id)
|
||||||
mastodonController.run(request) { (response) in
|
mastodonController.run(request) { (response) in
|
||||||
guard case let .success(accounts, _) = response else { fatalError() }
|
guard case let .success(accounts, _) = response else { fatalError() }
|
||||||
self.mastodonController.persistentContainer.addAll(accounts: accounts)
|
self.mastodonController.persistentContainer.addAll(accounts: accounts) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.accountIDs = accounts.map { $0.id }
|
self.accountIDs = accounts.map { $0.id }
|
||||||
self.tableView.tableFooterView = nil
|
self.tableView.tableFooterView = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,6 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
var newer: RequestRange?
|
var newer: RequestRange?
|
||||||
var older: RequestRange?
|
var older: RequestRange?
|
||||||
|
|
||||||
private var prevScrollViewContentOffset: CGPoint?
|
|
||||||
private var scrollViewDirection: CGFloat = 0
|
|
||||||
|
|
||||||
init(for timeline: Timeline, mastodonController: MastodonController) {
|
init(for timeline: Timeline, mastodonController: MastodonController) {
|
||||||
self.timeline = timeline
|
self.timeline = timeline
|
||||||
self.mastodonController = mastodonController
|
self.mastodonController = mastodonController
|
||||||
|
@ -109,8 +106,9 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
// MARK: - Table view delegate
|
// MARK: - Table view delegate
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
|
// don't remove rows when jumping to the top, otherwise jumping back down might try to show removed rows
|
||||||
// when scrolling upwards, decrement reference counts for old statuses, if necessary
|
// when scrolling upwards, decrement reference counts for old statuses, if necessary
|
||||||
if scrollViewDirection < 0 {
|
if !isCurrentlyScrollingToTop, scrollViewDirection < 0 {
|
||||||
if indexPath.section <= timelineSegments.count - 2 {
|
if indexPath.section <= timelineSegments.count - 2 {
|
||||||
// decrement ref counts for all sections below the section below the current section
|
// decrement ref counts for all sections below the section below the current section
|
||||||
// (e.g., there exist sections 0, 1, 2 and we're currently scrolling upwards in section 0, we want to remove section 2)
|
// (e.g., there exist sections 0, 1, 2 and we're currently scrolling upwards in section 0, we want to remove section 2)
|
||||||
|
@ -142,7 +140,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
for (id, _) in statusesToRemove {
|
for (id, _) in statusesToRemove {
|
||||||
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
||||||
}
|
}
|
||||||
timelineSegments[timelineSegments.count - 1].removeLast(20)
|
timelineSegments[timelineSegments.count - 1].removeLast(pageSize)
|
||||||
|
|
||||||
let removedIndexPaths = (lastSection.count - 20..<lastSection.count).map { IndexPath(row: $0, section: timelineSegments.count - 1) }
|
let removedIndexPaths = (lastSection.count - 20..<lastSection.count).map { IndexPath(row: $0, section: timelineSegments.count - 1) }
|
||||||
// Removing this DispatchQueue.main.async call causes things to break when scrolling
|
// Removing this DispatchQueue.main.async call causes things to break when scrolling
|
||||||
|
@ -196,6 +194,8 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.trailingSwipeActionsConfiguration()
|
return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.trailingSwipeActionsConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Interaction
|
||||||
|
|
||||||
@objc func refreshStatuses(_ sender: Any) {
|
@objc func refreshStatuses(_ sender: Any) {
|
||||||
guard let newer = newer else { return }
|
guard let newer = newer else { return }
|
||||||
|
|
||||||
|
@ -226,15 +226,6 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark: Scroll View Delegate
|
|
||||||
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
|
||||||
if let prev = prevScrollViewContentOffset {
|
|
||||||
scrollViewDirection = scrollView.contentOffset.y - prev.y
|
|
||||||
}
|
|
||||||
prevScrollViewContentOffset = scrollView.contentOffset
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@objc func composePressed(_ sender: Any) {
|
@objc func composePressed(_ sender: Any) {
|
||||||
compose()
|
compose()
|
||||||
|
|
|
@ -11,32 +11,45 @@ import SafariServices
|
||||||
|
|
||||||
class EnhancedTableViewController: UITableViewController {
|
class EnhancedTableViewController: UITableViewController {
|
||||||
|
|
||||||
var prevScrollToTopOffset: CGPoint? = nil
|
private var prevScrollToTopOffset: CGPoint? = nil
|
||||||
|
private(set) var isCurrentlyScrollingToTop = false
|
||||||
private var topOffset: CGPoint {
|
private var prevScrollViewContentOffset: CGPoint?
|
||||||
// when scrolled to top, the content offset is negative the height of the UI above the scroll view (i.e. the nav and status bars)
|
private(set) var scrollViewDirection: CGFloat = 0
|
||||||
let windowScene = view.window!.windowScene!
|
|
||||||
let barOffset = -1 * (navigationController!.navigationBar.frame.height + windowScene.statusBarManager!.statusBarFrame.height)
|
// MARK: Scroll View Delegate
|
||||||
// add one so it's not technically all the way at the top, and scrollViewWShouldScrollToTop is still called to trigger undo
|
|
||||||
return CGPoint(x: 0, y: barOffset + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
|
override func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
|
||||||
if let offset = prevScrollToTopOffset {
|
if let offset = prevScrollToTopOffset {
|
||||||
tableView.setContentOffset(offset, animated: true)
|
tableView.setContentOffset(offset, animated: true)
|
||||||
prevScrollToTopOffset = nil
|
prevScrollToTopOffset = nil
|
||||||
|
return false
|
||||||
} else {
|
} else {
|
||||||
prevScrollToTopOffset = tableView.contentOffset
|
prevScrollToTopOffset = tableView.contentOffset
|
||||||
tableView.setContentOffset(topOffset, animated: true)
|
isCurrentlyScrollingToTop = true
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return false
|
}
|
||||||
|
|
||||||
|
override func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
|
||||||
|
isCurrentlyScrollingToTop = false
|
||||||
|
// add one so it's not technically scrolled all the way to the top,
|
||||||
|
// otherwise there's no way of detecting a status bar press to scroll back down
|
||||||
|
tableView.contentOffset.y -= 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
prevScrollToTopOffset = nil
|
prevScrollToTopOffset = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
if let prev = prevScrollViewContentOffset {
|
||||||
|
scrollViewDirection = scrollView.contentOffset.y - prev.y
|
||||||
|
}
|
||||||
|
prevScrollViewContentOffset = scrollView.contentOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Table View Delegate
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
if let cell = tableView.cellForRow(at: indexPath) as? SelectableTableViewCell {
|
if let cell = tableView.cellForRow(at: indexPath) as? SelectableTableViewCell {
|
||||||
cell.didSelectCell()
|
cell.didSelectCell()
|
||||||
|
|
Loading…
Reference in New Issue