Add 3d touch peek/pop navigation

This commit is contained in:
Shadowfacts 2018-10-11 21:20:58 -04:00
parent 5f503cafb0
commit 0b6459a806
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
16 changed files with 280 additions and 105 deletions

View File

@ -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 */,

View File

@ -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()
} }

View File

@ -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) {

View File

@ -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
} }

View File

@ -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) {

View File

@ -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) {

View File

@ -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)
// }
// }
//
//}

View File

@ -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
}

View File

@ -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)
} }

View File

@ -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)
} }
} }

View File

@ -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)
} }
} }

View File

@ -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"/>

View File

@ -96,3 +96,9 @@ class FollowNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
} }
} }
extension FollowNotificationTableViewCell: PreviewViewControllerProvider {
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
return delegate?.viewController(forAccount: accountID)
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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)
}
}