Add 3d touch peek/pop navigation
This commit is contained in:
parent
76beb8ddbe
commit
d0feece606
|
@ -125,6 +125,7 @@
|
||||||
D6D4DDDA212518A200E1C4BB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6D4DDD8212518A200E1C4BB /* LaunchScreen.storyboard */; };
|
D6D4DDDA212518A200E1C4BB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6D4DDD8212518A200E1C4BB /* LaunchScreen.storyboard */; };
|
||||||
D6D4DDE5212518A200E1C4BB /* TuskerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDE4212518A200E1C4BB /* TuskerTests.swift */; };
|
D6D4DDE5212518A200E1C4BB /* TuskerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDE4212518A200E1C4BB /* TuskerTests.swift */; };
|
||||||
D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */; };
|
D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */; };
|
||||||
|
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E0DC8D216EDF1E00369478 /* Previewing.swift */; };
|
||||||
D6E6F26321603F8B006A8599 /* CharacterCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26221603F8B006A8599 /* CharacterCounter.swift */; };
|
D6E6F26321603F8B006A8599 /* CharacterCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26221603F8B006A8599 /* CharacterCounter.swift */; };
|
||||||
D6E6F26521604242006A8599 /* CharacterCounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26421604242006A8599 /* CharacterCounterTests.swift */; };
|
D6E6F26521604242006A8599 /* CharacterCounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26421604242006A8599 /* CharacterCounterTests.swift */; };
|
||||||
D6F953EC212519E700CF0F2B /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */; };
|
D6F953EC212519E700CF0F2B /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */; };
|
||||||
|
@ -318,6 +319,7 @@
|
||||||
D6D4DDEB212518A200E1C4BB /* TuskerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TuskerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
D6D4DDEB212518A200E1C4BB /* TuskerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TuskerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerUITests.swift; sourceTree = "<group>"; };
|
D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerUITests.swift; sourceTree = "<group>"; };
|
||||||
D6D4DDF1212518A200E1C4BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
D6D4DDF1212518A200E1C4BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
D6E0DC8D216EDF1E00369478 /* Previewing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Previewing.swift; sourceTree = "<group>"; };
|
||||||
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounter.swift; sourceTree = "<group>"; };
|
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounter.swift; sourceTree = "<group>"; };
|
||||||
D6E6F26421604242006A8599 /* CharacterCounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounterTests.swift; sourceTree = "<group>"; };
|
D6E6F26421604242006A8599 /* CharacterCounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounterTests.swift; sourceTree = "<group>"; };
|
||||||
D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
|
D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -672,7 +674,6 @@
|
||||||
D6BED1722126661300F02DA0 /* Views */ = {
|
D6BED1722126661300F02DA0 /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D621544921682AC60003D87D /* Tab */,
|
|
||||||
04496BD621625361001F1B23 /* ContentLabel.swift */,
|
04496BD621625361001F1B23 /* ContentLabel.swift */,
|
||||||
D6C693F82162E4DB007D6A6D /* StatusContentLabel.swift */,
|
D6C693F82162E4DB007D6A6D /* StatusContentLabel.swift */,
|
||||||
D6C94D882139E6EC00CB5196 /* AttachmentView.swift */,
|
D6C94D882139E6EC00CB5196 /* AttachmentView.swift */,
|
||||||
|
@ -683,6 +684,7 @@
|
||||||
D641C78A213DD926004B4513 /* Status */,
|
D641C78A213DD926004B4513 /* Status */,
|
||||||
D641C78B213DD92F004B4513 /* Profile Header */,
|
D641C78B213DD92F004B4513 /* Profile Header */,
|
||||||
D641C78C213DD937004B4513 /* Notifications */,
|
D641C78C213DD937004B4513 /* Notifications */,
|
||||||
|
D621544921682AC60003D87D /* Tab */,
|
||||||
D6C693CB2161256B007D6A6D /* Silent Action Permissions */,
|
D6C693CB2161256B007D6A6D /* Silent Action Permissions */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
|
@ -702,6 +704,7 @@
|
||||||
children = (
|
children = (
|
||||||
D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */,
|
D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */,
|
||||||
D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */,
|
D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */,
|
||||||
|
D6E0DC8D216EDF1E00369478 /* Previewing.swift */,
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1117,6 +1120,7 @@
|
||||||
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
||||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */,
|
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */,
|
||||||
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
||||||
|
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */,
|
||||||
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */,
|
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */,
|
||||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */,
|
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */,
|
||||||
D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */,
|
D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */,
|
||||||
|
|
|
@ -44,6 +44,10 @@ class ImageCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func get(_ url: URL) -> UIImage? {
|
||||||
|
return try? storage.object(forKey: url.absoluteString)
|
||||||
|
}
|
||||||
|
|
||||||
func cancel(_ url: URL) {
|
func cancel(_ url: URL) {
|
||||||
requests[url]?.cancel()
|
requests[url]?.cancel()
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,8 @@ class ConversationViewController: UITableViewController {
|
||||||
self.tableView.scrollToRow(at: indexPath, at: .middle, animated: false)
|
self.tableView.scrollToRow(at: indexPath, at: .middle, animated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerForPreviewing(with: self, sourceView: view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
|
|
@ -15,10 +15,22 @@ protocol LargeImageViewControllerDelegate {
|
||||||
|
|
||||||
class LargeImageViewController: UIViewController, UIScrollViewDelegate {
|
class LargeImageViewController: UIViewController, UIScrollViewDelegate {
|
||||||
|
|
||||||
static func create(image: UIImage, description: String?) -> LargeImageViewController {
|
static func create(image: UIImage, description: String?, sourceView: UIView, sourceViewController: UIViewController) -> LargeImageViewController {
|
||||||
guard let vc = UIStoryboard(name: "LargeImage", bundle: nil).instantiateInitialViewController() as? LargeImageViewController else { fatalError() }
|
guard let vc = UIStoryboard(name: "LargeImage", bundle: nil).instantiateInitialViewController() as? LargeImageViewController else { fatalError() }
|
||||||
vc.image = image
|
vc.image = image
|
||||||
vc.imageDescription = description
|
vc.imageDescription = description
|
||||||
|
var frame = sourceView.convert(sourceView.bounds, to: sourceViewController.view)
|
||||||
|
if let scrollView = sourceViewController.view as? UIScrollView {
|
||||||
|
let scale = scrollView.zoomScale
|
||||||
|
let width = frame.width * scale
|
||||||
|
let height = frame.height * scale
|
||||||
|
let x = frame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX
|
||||||
|
let y = frame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
|
||||||
|
frame = CGRect(x: x, y: y, width: width, height: height)
|
||||||
|
}
|
||||||
|
vc.originFrame = frame
|
||||||
|
vc.originCornerRadius = sourceView.layer.cornerRadius
|
||||||
|
vc.transitioningDelegate = sourceViewController
|
||||||
return vc
|
return vc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,8 @@ class NotificationsTableViewController: UITableViewController {
|
||||||
self.newer = pagination?.newer
|
self.newer = pagination?.newer
|
||||||
self.older = pagination?.older
|
self.older = pagination?.older
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerForPreviewing(with: self, sourceView: view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
|
|
@ -76,6 +76,8 @@ class TimelineTableViewController: UITableViewController {
|
||||||
self.newer = pagination?.newer
|
self.newer = pagination?.newer
|
||||||
self.older = pagination?.older
|
self.older = pagination?.older
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerForPreviewing(with: self, sourceView: view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
//
|
||||||
|
// PreviewViewControllerProvider.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 10/10/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
enum PreviewCommitType {
|
||||||
|
case nav
|
||||||
|
case modal
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol PreviewViewControllerProvider {
|
||||||
|
|
||||||
|
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController?
|
||||||
|
|
||||||
|
func getPreviewCommitType(forViewController viewController: UIViewController) -> PreviewCommitType
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PreviewViewControllerProvider {
|
||||||
|
func getPreviewCommitType(forViewController viewController: UIViewController) -> PreviewCommitType {
|
||||||
|
return viewController is LargeImageViewController ? .modal : .nav
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc extension UITableViewController: UIViewControllerPreviewingDelegate {
|
||||||
|
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
|
||||||
|
if let indexPath = tableView.indexPathForRow(at: location),
|
||||||
|
let cell = tableView.cellForRow(at: indexPath) as? UITableViewCell & PreviewViewControllerProvider {
|
||||||
|
let cellLocation = cell.convert(location, from: tableView)
|
||||||
|
if let vc = cell.getPreviewViewController(forLocation: cellLocation, sourceViewController: self) {
|
||||||
|
previewingContext.sourceRect = tableView.rectForRow(at: indexPath)
|
||||||
|
return vc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
|
||||||
|
if viewControllerToCommit is LargeImageViewController {
|
||||||
|
present(viewControllerToCommit, animated: false)
|
||||||
|
} else {
|
||||||
|
navigationController!.pushViewController(viewControllerToCommit, animated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//class PreviewingController: NSObject, UIViewControllerPreviewingDelegate {
|
||||||
|
//
|
||||||
|
// var currentCommitType: PreviewCommitType?
|
||||||
|
// var owner: UIViewController?
|
||||||
|
//
|
||||||
|
// init(owner: UIViewController) {
|
||||||
|
// self.owner = owner
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
|
||||||
|
// if let owner = owner as? UITableViewController,
|
||||||
|
// let indexPath = owner.tableView.indexPathForRow(at: location),
|
||||||
|
// let cell = owner.tableView.cellForRow(at: indexPath) as? UITableViewCell & PreviewViewControllerProvider {
|
||||||
|
// let cellLocation = cell.convert(location, from: owner.tableView)
|
||||||
|
// if let vc = cell.getPreviewViewController(forLocation: cellLocation, sourceViewController: owner) {
|
||||||
|
// currentCommitType = cell.getPreviewCommitType(forViewController: vc)
|
||||||
|
// previewingContext.sourceRect = owner.tableView.rectForRow(at: indexPath)
|
||||||
|
// return vc
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
|
||||||
|
// switch currentCommitType ?? .nav {
|
||||||
|
// case .modal:
|
||||||
|
// owner?.present(viewControllerToCommit, animated: false)
|
||||||
|
// case .nav:
|
||||||
|
// owner?.navigationController!.pushViewController(viewControllerToCommit, animated: false)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
|
@ -0,0 +1,15 @@
|
||||||
|
//
|
||||||
|
// PreviewingController.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 10/11/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class PreviewingController {
|
||||||
|
|
||||||
|
var currentCommitType: PreviewCommitType
|
||||||
|
|
||||||
|
}
|
|
@ -11,18 +11,30 @@ import SafariServices
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
protocol TuskerNavigationDelegate {
|
protocol TuskerNavigationDelegate {
|
||||||
|
func viewController(forAccount accountID: String) -> UIViewController
|
||||||
|
|
||||||
func selected(account accountID: String)
|
func selected(account accountID: String)
|
||||||
|
|
||||||
|
func viewController(forMention mention: Mention) -> UIViewController
|
||||||
|
|
||||||
func selected(mention: Mention)
|
func selected(mention: Mention)
|
||||||
|
|
||||||
|
func viewController(forTag tag: Hashtag) -> UIViewController
|
||||||
|
|
||||||
func selected(tag: Hashtag)
|
func selected(tag: Hashtag)
|
||||||
|
|
||||||
|
func viewController(forURL url: URL) -> UIViewController
|
||||||
|
|
||||||
func selected(url: URL)
|
func selected(url: URL)
|
||||||
|
|
||||||
|
func viewController(forStatus statusID: String) -> UIViewController
|
||||||
|
|
||||||
func selected(status statusID: String)
|
func selected(status statusID: String)
|
||||||
|
|
||||||
func reply(to statusID: String)
|
func reply(to statusID: String)
|
||||||
|
|
||||||
|
func viewController(forImage image: UIImage, description: String?, animatingFrom originView: UIView) -> UIViewController
|
||||||
|
|
||||||
func showLargeImage(_ image: UIImage, description: String?, animatingFrom originView: UIView)
|
func showLargeImage(_ image: UIImage, description: String?, animatingFrom originView: UIView)
|
||||||
|
|
||||||
func showMoreOptions(forStatus statusID: String)
|
func showMoreOptions(forStatus statusID: String)
|
||||||
|
@ -30,6 +42,10 @@ protocol TuskerNavigationDelegate {
|
||||||
|
|
||||||
extension TuskerNavigationDelegate where Self: UIViewController {
|
extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
|
|
||||||
|
func viewController(forAccount accountID: String) -> UIViewController {
|
||||||
|
return ProfileTableViewController.create(for: accountID)
|
||||||
|
}
|
||||||
|
|
||||||
func selected(account accountID: String) {
|
func selected(account accountID: String) {
|
||||||
// don't open if the account is the same as the current one
|
// don't open if the account is the same as the current one
|
||||||
if let profileController = self as? ProfileTableViewController,
|
if let profileController = self as? ProfileTableViewController,
|
||||||
|
@ -40,32 +56,48 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
guard let navigationController = navigationController else {
|
guard let navigationController = navigationController else {
|
||||||
fatalError("Can't show profile VC when not in navigation controller")
|
fatalError("Can't show profile VC when not in navigation controller")
|
||||||
}
|
}
|
||||||
let vc = ProfileTableViewController.create(for: accountID)
|
let vc = viewController(forAccount: accountID)
|
||||||
navigationController.pushViewController(vc, animated: true)
|
navigationController.pushViewController(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func viewController(forMention mention: Mention) -> UIViewController {
|
||||||
|
return ProfileTableViewController.create(for: mention.id)
|
||||||
|
}
|
||||||
|
|
||||||
func selected(mention: Mention) {
|
func selected(mention: Mention) {
|
||||||
guard let navigationController = navigationController else {
|
guard let navigationController = navigationController else {
|
||||||
fatalError("Can't show profile VC from mention when not in navigation controller")
|
fatalError("Can't show profile VC from mention when not in navigation controller")
|
||||||
}
|
}
|
||||||
let vc = ProfileTableViewController.create(for: mention.id)
|
let vc = viewController(forMention: mention)
|
||||||
navigationController.pushViewController(vc, animated: true)
|
navigationController.pushViewController(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func viewController(forTag tag: Hashtag) -> UIViewController {
|
||||||
|
let timeline = Timeline.tag(hashtag: tag.name)
|
||||||
|
return TimelineTableViewController.create(for: timeline)
|
||||||
|
}
|
||||||
|
|
||||||
func selected(tag: Hashtag) {
|
func selected(tag: Hashtag) {
|
||||||
guard let navigationController = navigationController else {
|
guard let navigationController = navigationController else {
|
||||||
fatalError("Can't show hashtag timeline when not in navigation controller")
|
fatalError("Can't show hashtag timeline when not in navigation controller")
|
||||||
}
|
}
|
||||||
let timeline = Timeline.tag(hashtag: tag.name)
|
let vc = viewController(forTag: tag)
|
||||||
let vc = TimelineTableViewController.create(for: timeline)
|
|
||||||
navigationController.pushViewController(vc, animated: true)
|
navigationController.pushViewController(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func viewController(forURL url: URL) -> UIViewController {
|
||||||
|
return SFSafariViewController(url: url)
|
||||||
|
}
|
||||||
|
|
||||||
func selected(url: URL) {
|
func selected(url: URL) {
|
||||||
let vc = SFSafariViewController(url: url)
|
let vc = viewController(forURL: url)
|
||||||
present(vc, animated: true)
|
present(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func viewController(forStatus statusID: String) -> UIViewController {
|
||||||
|
return ConversationViewController.create(for: statusID)
|
||||||
|
}
|
||||||
|
|
||||||
func selected(status statusID: String) {
|
func selected(status statusID: String) {
|
||||||
// don't open if the conversation is the same as the current one
|
// don't open if the conversation is the same as the current one
|
||||||
if let conversationController = self as? ConversationViewController,
|
if let conversationController = self as? ConversationViewController,
|
||||||
|
@ -76,7 +108,7 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
guard let navigationController = navigationController else {
|
guard let navigationController = navigationController else {
|
||||||
fatalError("Can't show conversation VC when not in navigation controller")
|
fatalError("Can't show conversation VC when not in navigation controller")
|
||||||
}
|
}
|
||||||
let vc = ConversationViewController.create(for: statusID)
|
let vc = viewController(forStatus: statusID)
|
||||||
navigationController.pushViewController(vc, animated: true)
|
navigationController.pushViewController(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,22 +117,17 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
present(vc, animated: true)
|
present(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showLargeImage(_ image: UIImage, description: String?, animatingFrom originView: UIView) {
|
func viewController(forImage image: UIImage, description: String?, animatingFrom originView: UIView) -> UIViewController {
|
||||||
guard let self = self as? UIViewController & LargeImageViewControllerDelegate else { return }
|
guard let self = self as? UIViewController & LargeImageViewControllerDelegate else {
|
||||||
let vc = LargeImageViewController.create(image: image, description: description)
|
fatalError("Can't create large image view controller unless self is LargeImageViewControllerDelegate")
|
||||||
vc.delegate = self
|
|
||||||
var frame = originView.convert(originView.bounds, to: view)
|
|
||||||
if let scrollView = view as? UIScrollView {
|
|
||||||
let scale = scrollView.zoomScale
|
|
||||||
let width = frame.width * scale
|
|
||||||
let height = frame.height * scale
|
|
||||||
let x = frame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX
|
|
||||||
let y = frame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
|
|
||||||
frame = CGRect(x: x, y: y, width: width, height: height)
|
|
||||||
}
|
}
|
||||||
vc.originFrame = frame
|
let vc = LargeImageViewController.create(image: image, description: description, sourceView: originView, sourceViewController: self)
|
||||||
vc.originCornerRadius = originView.layer.cornerRadius
|
vc.delegate = self
|
||||||
vc.transitioningDelegate = self
|
return vc
|
||||||
|
}
|
||||||
|
|
||||||
|
func showLargeImage(_ image: UIImage, description: String?, animatingFrom originView: UIView) {
|
||||||
|
let vc = viewController(forImage: image, description: description, animatingFrom: originView)
|
||||||
present(vc, animated: true)
|
present(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,17 +11,9 @@ import TTTAttributedLabel
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import SwiftSoup
|
import SwiftSoup
|
||||||
|
|
||||||
protocol ContentLabelNavigationDelegate {
|
|
||||||
func selected(mention: Mention)
|
|
||||||
|
|
||||||
func selected(tag: Hashtag)
|
|
||||||
|
|
||||||
func selected(url: URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ContentLabel: TTTAttributedLabel {
|
class ContentLabel: TTTAttributedLabel {
|
||||||
|
|
||||||
var navigationDelegate: ContentLabelNavigationDelegate?
|
var navigationDelegate: TuskerNavigationDelegate?
|
||||||
|
|
||||||
override func awakeFromNib() {
|
override func awakeFromNib() {
|
||||||
super.awakeFromNib()
|
super.awakeFromNib()
|
||||||
|
@ -92,18 +84,35 @@ class ContentLabel: TTTAttributedLabel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getViewController(forLinkAt point: CGPoint) -> UIViewController? {
|
||||||
|
guard let navigationDelegate = navigationDelegate,
|
||||||
|
let link = link(at: point),
|
||||||
|
let url = link.result.url else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let text = (self.text as! NSString).substring(with: link.result.range)
|
||||||
|
if let mention = getMention(for: url, text: text) {
|
||||||
|
return navigationDelegate.viewController(forMention: mention)
|
||||||
|
} else if let tag = getHashtag(for: url, text: text) {
|
||||||
|
return navigationDelegate.viewController(forTag: tag)
|
||||||
|
} else {
|
||||||
|
return navigationDelegate.viewController(forURL: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Interaction
|
// MARK: - Interaction
|
||||||
func linkTapped(_ label: TTTAttributedLabel!, _ link: TTTAttributedLabelLink!) {
|
func linkTapped(_ label: TTTAttributedLabel!, _ link: TTTAttributedLabelLink!) {
|
||||||
if let navigationDelegate = navigationDelegate,
|
guard let navigationDelegate = navigationDelegate,
|
||||||
let url = link.result.url {
|
let url = link.result.url else {
|
||||||
let text = (self.text as! NSString).substring(with: link.result.range)
|
return
|
||||||
if let mention = getMention(for: url, text: text) {
|
}
|
||||||
navigationDelegate.selected(mention: mention)
|
let text = (self.text as! NSString).substring(with: link.result.range)
|
||||||
} else if let tag = getHashtag(for: url, text: text) {
|
if let mention = getMention(for: url, text: text) {
|
||||||
navigationDelegate.selected(tag: tag)
|
navigationDelegate.selected(mention: mention)
|
||||||
} else {
|
} else if let tag = getHashtag(for: url, text: text) {
|
||||||
navigationDelegate.selected(url: url)
|
navigationDelegate.selected(tag: tag)
|
||||||
}
|
} else {
|
||||||
|
navigationDelegate.selected(url: url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,16 @@ import Pachyderm
|
||||||
|
|
||||||
class ActionNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
class ActionNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
|
|
||||||
var delegate: StatusTableViewCellDelegate?
|
var delegate: StatusTableViewCellDelegate? {
|
||||||
|
didSet {
|
||||||
|
contentLabel.navigationDelegate = delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@IBOutlet weak var displayNameLabel: UILabel!
|
@IBOutlet weak var displayNameLabel: UILabel!
|
||||||
@IBOutlet weak var usernameLabel: UILabel!
|
@IBOutlet weak var usernameLabel: UILabel!
|
||||||
@IBOutlet weak var contentLabel: StatusContentLabel!
|
@IBOutlet weak var contentLabel: StatusContentLabel!
|
||||||
|
@IBOutlet weak var avatarContainerView: UIView!
|
||||||
@IBOutlet weak var opAvatarImageView: UIImageView!
|
@IBOutlet weak var opAvatarImageView: UIImageView!
|
||||||
@IBOutlet weak var actionAvatarImageView: UIImageView!
|
@IBOutlet weak var actionAvatarImageView: UIImageView!
|
||||||
@IBOutlet weak var actionLabel: UILabel!
|
@IBOutlet weak var actionLabel: UILabel!
|
||||||
|
@ -40,7 +45,6 @@ class ActionNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
actionAvatarImageView.layer.masksToBounds = true
|
actionAvatarImageView.layer.masksToBounds = true
|
||||||
actionLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(actionPressed)))
|
actionLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(actionPressed)))
|
||||||
actionLabel.isUserInteractionEnabled = true
|
actionLabel.isUserInteractionEnabled = true
|
||||||
contentLabel.navigationDelegate = self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIForPreferences() {
|
func updateUIForPreferences() {
|
||||||
|
@ -186,16 +190,14 @@ class ActionNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ActionNotificationTableViewCell: ContentLabelNavigationDelegate {
|
extension ActionNotificationTableViewCell: PreviewViewControllerProvider {
|
||||||
func selected(mention: Mention) {
|
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||||
delegate?.selected(mention: mention)
|
if avatarContainerView.frame.contains(location) {
|
||||||
}
|
return delegate?.viewController(forAccount: notification.account.id)
|
||||||
|
} else if contentLabel.frame.contains(location),
|
||||||
func selected(tag: Hashtag) {
|
let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) {
|
||||||
delegate?.selected(tag: tag)
|
return vc
|
||||||
}
|
}
|
||||||
|
return delegate?.viewController(forStatus: statusID)
|
||||||
func selected(url: URL) {
|
|
||||||
delegate?.selected(url: url)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.15" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.23.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="retina4_7" orientation="portrait">
|
<device id="retina4_7" orientation="portrait">
|
||||||
<adaptation id="fullscreen"/>
|
<adaptation id="fullscreen"/>
|
||||||
</device>
|
</device>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.9"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.16.1"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -124,6 +124,7 @@
|
||||||
<outlet property="actionAvatarImageView" destination="bXi-tl-kR9" id="QB8-ll-vNb"/>
|
<outlet property="actionAvatarImageView" destination="bXi-tl-kR9" id="QB8-ll-vNb"/>
|
||||||
<outlet property="actionLabel" destination="Cwu-6F-uNO" id="d7e-Za-Xed"/>
|
<outlet property="actionLabel" destination="Cwu-6F-uNO" id="d7e-Za-Xed"/>
|
||||||
<outlet property="attachmentsView" destination="HGa-49-qx0" id="x7p-uh-QRj"/>
|
<outlet property="attachmentsView" destination="HGa-49-qx0" id="x7p-uh-QRj"/>
|
||||||
|
<outlet property="avatarContainerView" destination="RTx-MR-PMy" id="Qri-Cd-kjN"/>
|
||||||
<outlet property="contentLabel" destination="30l-QK-uJH" id="eNc-Xt-C0E"/>
|
<outlet property="contentLabel" destination="30l-QK-uJH" id="eNc-Xt-C0E"/>
|
||||||
<outlet property="displayNameLabel" destination="wFQ-nU-BGD" id="MkH-di-Bgr"/>
|
<outlet property="displayNameLabel" destination="wFQ-nU-BGD" id="MkH-di-Bgr"/>
|
||||||
<outlet property="opAvatarImageView" destination="BE8-ts-R0p" id="cu8-Kt-rbM"/>
|
<outlet property="opAvatarImageView" destination="BE8-ts-R0p" id="cu8-Kt-rbM"/>
|
||||||
|
|
|
@ -96,3 +96,9 @@ class FollowNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension FollowNotificationTableViewCell: PreviewViewControllerProvider {
|
||||||
|
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||||
|
return delegate?.viewController(forAccount: accountID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,11 @@ protocol ProfileHeaderTableViewCellDelegate: TuskerNavigationDelegate {
|
||||||
|
|
||||||
class ProfileHeaderTableViewCell: UITableViewCell, PreferencesAdaptive {
|
class ProfileHeaderTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
|
|
||||||
var delegate: ProfileHeaderTableViewCellDelegate?
|
var delegate: ProfileHeaderTableViewCellDelegate? {
|
||||||
|
didSet {
|
||||||
|
noteLabel.navigationDelegate = delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@IBOutlet weak var headerImageView: UIImageView!
|
@IBOutlet weak var headerImageView: UIImageView!
|
||||||
@IBOutlet weak var avatarContainerView: UIView!
|
@IBOutlet weak var avatarContainerView: UIView!
|
||||||
|
@ -37,7 +41,6 @@ class ProfileHeaderTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
avatarImageView.isUserInteractionEnabled = true
|
avatarImageView.isUserInteractionEnabled = true
|
||||||
headerImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerPressed)))
|
headerImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerPressed)))
|
||||||
headerImageView.isUserInteractionEnabled = true
|
headerImageView.isUserInteractionEnabled = true
|
||||||
noteLabel.navigationDelegate = self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIForPreferences() {
|
func updateUIForPreferences() {
|
||||||
|
@ -109,17 +112,3 @@ class ProfileHeaderTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProfileHeaderTableViewCell: ContentLabelNavigationDelegate {
|
|
||||||
func selected(mention: Mention) {
|
|
||||||
delegate?.selected(mention: mention)
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(tag: Hashtag) {
|
|
||||||
delegate?.selected(tag: tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(url: URL) {
|
|
||||||
delegate?.selected(url: url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,7 +11,11 @@ import Pachyderm
|
||||||
|
|
||||||
class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
|
|
||||||
var delegate: StatusTableViewCellDelegate?
|
var delegate: StatusTableViewCellDelegate? {
|
||||||
|
didSet {
|
||||||
|
contentLabel.navigationDelegate = delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@IBOutlet weak var displayNameLabel: UILabel!
|
@IBOutlet weak var displayNameLabel: UILabel!
|
||||||
@IBOutlet weak var usernameLabel: UILabel!
|
@IBOutlet weak var usernameLabel: UILabel!
|
||||||
|
@ -49,7 +53,6 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
|
||||||
avatarImageView.layer.masksToBounds = true
|
avatarImageView.layer.masksToBounds = true
|
||||||
attachmentsView.layer.cornerRadius = 5
|
attachmentsView.layer.cornerRadius = 5
|
||||||
attachmentsView.layer.masksToBounds = true
|
attachmentsView.layer.masksToBounds = true
|
||||||
contentLabel.navigationDelegate = self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIForPreferences() {
|
func updateUIForPreferences() {
|
||||||
|
@ -216,22 +219,27 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ConversationMainStatusTableViewCell: ContentLabelNavigationDelegate {
|
|
||||||
func selected(mention: Mention) {
|
|
||||||
delegate?.selected(mention: mention)
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(tag: Hashtag) {
|
|
||||||
delegate?.selected(tag: tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(url: URL) {
|
|
||||||
delegate?.selected(url: url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ConversationMainStatusTableViewCell: AttachmentViewDelegate {
|
extension ConversationMainStatusTableViewCell: AttachmentViewDelegate {
|
||||||
func showLargeAttachment(for attachmentView: AttachmentView) {
|
func showLargeAttachment(for attachmentView: AttachmentView) {
|
||||||
delegate?.showLargeImage(attachmentView.image!, description: attachmentView.attachment.description, animatingFrom: attachmentView)
|
delegate?.showLargeImage(attachmentView.image!, description: attachmentView.attachment.description, animatingFrom: attachmentView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ConversationMainStatusTableViewCell: PreviewViewControllerProvider {
|
||||||
|
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||||
|
if avatarImageView.frame.contains(location) {
|
||||||
|
return delegate?.viewController(forAccount: accountID)
|
||||||
|
} else if attachmentsView.frame.contains(location) {
|
||||||
|
let attachmentsViewLocation = attachmentsView.convert(location, from: self)
|
||||||
|
if let attachmentView = attachmentsView.subviews.first(where: { $0.frame.contains(attachmentsViewLocation) }) as? AttachmentView {
|
||||||
|
let image = attachmentView.image!
|
||||||
|
let description = attachmentView.description
|
||||||
|
return delegate?.viewController(forImage: image, description: description, animatingFrom: attachmentView)
|
||||||
|
}
|
||||||
|
} else if contentLabel.frame.contains(location),
|
||||||
|
let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) {
|
||||||
|
return vc
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,11 @@ protocol StatusTableViewCellDelegate: TuskerNavigationDelegate {
|
||||||
|
|
||||||
class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
|
|
||||||
var delegate: StatusTableViewCellDelegate?
|
var delegate: StatusTableViewCellDelegate? {
|
||||||
|
didSet {
|
||||||
|
contentLabel.navigationDelegate = delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@IBOutlet weak var displayNameLabel: UILabel!
|
@IBOutlet weak var displayNameLabel: UILabel!
|
||||||
@IBOutlet weak var usernameLabel: UILabel!
|
@IBOutlet weak var usernameLabel: UILabel!
|
||||||
|
@ -57,7 +61,6 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||||
avatarImageView.layer.masksToBounds = true
|
avatarImageView.layer.masksToBounds = true
|
||||||
attachmentsView.layer.cornerRadius = 5
|
attachmentsView.layer.cornerRadius = 5
|
||||||
attachmentsView.layer.masksToBounds = true
|
attachmentsView.layer.masksToBounds = true
|
||||||
contentLabel.navigationDelegate = self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIForPreferences() {
|
func updateUIForPreferences() {
|
||||||
|
@ -345,22 +348,27 @@ extension StatusTableViewCell: TableViewSwipeActionProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StatusTableViewCell: ContentLabelNavigationDelegate {
|
|
||||||
func selected(mention: Mention) {
|
|
||||||
delegate?.selected(mention: mention)
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(tag: Hashtag) {
|
|
||||||
delegate?.selected(tag: tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func selected(url: URL) {
|
|
||||||
delegate?.selected(url: url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension StatusTableViewCell: AttachmentViewDelegate {
|
extension StatusTableViewCell: AttachmentViewDelegate {
|
||||||
func showLargeAttachment(for attachmentView: AttachmentView) {
|
func showLargeAttachment(for attachmentView: AttachmentView) {
|
||||||
delegate?.showLargeImage(attachmentView.image!, description: attachmentView.attachment.description, animatingFrom: attachmentView)
|
delegate?.showLargeImage(attachmentView.image!, description: attachmentView.attachment.description, animatingFrom: attachmentView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension StatusTableViewCell: PreviewViewControllerProvider {
|
||||||
|
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||||
|
if avatarImageView.frame.contains(location) {
|
||||||
|
return delegate?.viewController(forAccount: accountID)
|
||||||
|
} else if attachmentsView.frame.contains(location) {
|
||||||
|
let attachmentsViewLocation = attachmentsView.convert(location, from: self)
|
||||||
|
if let attachmentView = attachmentsView.subviews.first(where: { $0.frame.contains(attachmentsViewLocation) }) as? AttachmentView {
|
||||||
|
let image = attachmentView.image!
|
||||||
|
let description = attachmentView.attachment.description
|
||||||
|
return delegate?.viewController(forImage: image, description: description, animatingFrom: attachmentView)
|
||||||
|
}
|
||||||
|
} else if contentLabel.frame.contains(location),
|
||||||
|
let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) {
|
||||||
|
return vc
|
||||||
|
}
|
||||||
|
return delegate?.viewController(forStatus: statusID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue