Add support for iOS 13 previewing and actions
This commit is contained in:
parent
2c452b08e8
commit
a89fb56a60
|
@ -58,8 +58,6 @@ class ConversationTableViewController: EnhancedTableViewController {
|
|||
self.tableView.scrollToRow(at: indexPath, at: .middle, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
registerForPreviewing(with: self, sourceView: view)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
|
|
@ -56,8 +56,6 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
|||
self.older = pagination?.older
|
||||
}
|
||||
|
||||
registerForPreviewing(with: self, sourceView: view)
|
||||
|
||||
userActivity = UserActivityManager.checkNotificationsActivity()
|
||||
}
|
||||
|
||||
|
|
|
@ -89,8 +89,6 @@ class ProfileTableViewController: EnhancedTableViewController, PreferencesAdapti
|
|||
add(loadingVC!)
|
||||
shouldLoadOnAccountIDSet = true
|
||||
}
|
||||
|
||||
registerForPreviewing(with: self, sourceView: view)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
|
|
@ -82,8 +82,6 @@ class TimelineTableViewController: EnhancedTableViewController {
|
|||
self.newer = pagination?.newer
|
||||
self.older = pagination?.older
|
||||
}
|
||||
|
||||
registerForPreviewing(with: self, sourceView: view)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
@ -119,6 +117,8 @@ class TimelineTableViewController: EnhancedTableViewController {
|
|||
return cell
|
||||
}
|
||||
|
||||
// MARK: - Table view delegate
|
||||
|
||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
if indexPath.row == statusIDs.count - 1 {
|
||||
guard let older = older else { return }
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
|
||||
class EnhancedTableViewController: UITableViewController {
|
||||
|
||||
|
@ -36,3 +37,35 @@ class EnhancedTableViewController: UITableViewController {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
extension EnhancedTableViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
||||
if let cell = tableView.cellForRow(at: indexPath) as? UITableViewCell & MenuPreviewProvider {
|
||||
let cellLocation = cell.convert(point, from: tableView)
|
||||
guard let (previewProvider, actionsProvider) = cell.getPreviewProviders(for: cellLocation, sourceViewController: self) else {
|
||||
return nil
|
||||
}
|
||||
let actionProvider: UIContextMenuActionProvider = { (elements) in
|
||||
return UIMenu<UIAction>.create(title: "test", children: elements + actionsProvider())
|
||||
}
|
||||
return UIContextMenuConfiguration(identifier: nil, previewProvider: previewProvider, actionProvider: actionProvider)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, willCommitMenuWithAnimator animator: UIContextMenuInteractionCommitAnimating) {
|
||||
if /*animator.preferredCommitStyle == .pop,*/ // preferredCommitStyle is always .dismiss, see FB6113554
|
||||
let viewController = animator.previewViewController {
|
||||
animator.addCompletion {
|
||||
if viewController is LargeImageViewController || viewController is SFSafariViewController {
|
||||
self.present(viewController, animated: true)
|
||||
} else {
|
||||
self.show(viewController, sender: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ class LoadingViewController: UIViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
|
||||
activityIndicator = UIActivityIndicatorView(style: .large)
|
||||
activityIndicator.color = .darkGray
|
||||
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
|
|
|
@ -8,31 +8,65 @@
|
|||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import Pachyderm
|
||||
|
||||
protocol PreviewViewControllerProvider {
|
||||
protocol MenuPreviewProvider {
|
||||
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController?
|
||||
typealias PreviewProviders = (content: UIContextMenuContentPreviewProvider, actions: () -> [UIAction])
|
||||
|
||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders?
|
||||
|
||||
}
|
||||
|
||||
@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
|
||||
extension MenuPreviewProvider {
|
||||
|
||||
fileprivate func present(_ vc: UIViewController) {
|
||||
UIApplication.shared.keyWindow!.rootViewController!.present(vc, animated: true)
|
||||
}
|
||||
|
||||
func actionsForProfile(accountID: String) -> [UIAction] {
|
||||
guard let account = MastodonCache.account(for: accountID) else { return [] }
|
||||
return [
|
||||
UIAction(__title: "Open in Safari", image: UIImage(systemName: "safari")) { (_) in
|
||||
self.present(SFSafariViewController(url: account.url))
|
||||
},
|
||||
UIAction(__title: "Send Message", image: UIImage(systemName: "envelope")) { (_) in
|
||||
self.present(UINavigationController(rootViewController: ComposeViewController(mentioningAcct: account.acct)))
|
||||
},
|
||||
UIAction(__title: "Share...", image: UIImage(systemName: "square.and.arrow.up")) { (_) in
|
||||
self.present(UIActivityViewController(activityItems: [account.url], applicationActivities: nil))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
]
|
||||
}
|
||||
|
||||
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
|
||||
if viewControllerToCommit is LargeImageViewController || viewControllerToCommit is SFSafariViewController {
|
||||
present(viewControllerToCommit, animated: false)
|
||||
} else {
|
||||
navigationController!.pushViewController(viewControllerToCommit, animated: false)
|
||||
}
|
||||
func actionsForURL(_ url: URL) -> [UIAction] {
|
||||
return [
|
||||
UIAction(__title: "Open in Safari", image: UIImage(systemName: "safari")) { (_) in
|
||||
self.present(SFSafariViewController(url: url))
|
||||
},
|
||||
UIAction(__title: "Share...", image: UIImage(systemName: "square.and.arrow.up")) { (_) in
|
||||
self.present(UIActivityViewController(activityItems: [url], applicationActivities: nil))
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
func actionsForHashtag(_ hashtag: Hashtag) -> [UIAction] {
|
||||
return actionsForURL(hashtag.url)
|
||||
}
|
||||
|
||||
func actionsForStatus(statusID: String) -> [UIAction] {
|
||||
guard let status = MastodonCache.status(for: statusID) else { return [] }
|
||||
return [
|
||||
UIAction(__title: "Reply", image: UIImage(systemName: "arrowshape.turn.up.left")) { (_) in
|
||||
self.present(UINavigationController(rootViewController: ComposeViewController(inReplyTo: statusID)))
|
||||
},
|
||||
UIAction(__title: "Open in Safari", image: UIImage(systemName: "safari")) { (_) in
|
||||
self.present(SFSafariViewController(url: status.url!))
|
||||
},
|
||||
UIAction(__title: "Share...", image: UIImage(systemName: "square.and.arrow.up")) { (_) in
|
||||
self.present(UIActivityViewController(activityItems: [status.url!], applicationActivities: nil))
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -179,14 +179,27 @@ class ActionNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
|
||||
}
|
||||
|
||||
extension ActionNotificationTableViewCell: PreviewViewControllerProvider {
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||
extension ActionNotificationTableViewCell: MenuPreviewProvider {
|
||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
||||
if avatarContainerView.frame.contains(location) {
|
||||
return ProfileTableViewController(accountID: notification.account.id)
|
||||
let accountID = notification.account.id
|
||||
return (content: { ProfileTableViewController(accountID: accountID) }, actions: { self.actionsForProfile(accountID: accountID) })
|
||||
} else if contentLabel.frame.contains(location),
|
||||
let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) {
|
||||
return vc
|
||||
let link = contentLabel.getLink(atPoint: contentLabel.convert(location, from: self)) {
|
||||
return (
|
||||
content: { self.contentLabel.getViewController(forLink: link.url, inRange: link.range) },
|
||||
actions: {
|
||||
let text = (self.contentLabel.text! as NSString).substring(with: link.range)
|
||||
if let mention = self.contentLabel.getMention(for: link.url, text: text) {
|
||||
return self.actionsForProfile(accountID: mention.id)
|
||||
} else if let hashtag = self.contentLabel.getHashtag(for: link.url, text: text) {
|
||||
return self.actionsForHashtag(hashtag)
|
||||
} else {
|
||||
return self.actionsForURL(link.url)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
return ConversationTableViewController(for: statusID)
|
||||
return (content: { ConversationTableViewController(for: self.statusID) }, actions: { self.actionsForStatus(statusID: self.statusID) })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,8 +98,8 @@ class FollowNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
|
||||
}
|
||||
|
||||
extension FollowNotificationTableViewCell: PreviewViewControllerProvider {
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||
return ProfileTableViewController(accountID: accountID)
|
||||
extension FollowNotificationTableViewCell: MenuPreviewProvider {
|
||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
||||
return (content: { ProfileTableViewController(accountID: self.accountID) }, actions: { self.actionsForProfile(accountID: self.accountID) })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,14 +141,26 @@ class ProfileHeaderTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
|
||||
}
|
||||
|
||||
extension ProfileHeaderTableViewCell: PreviewViewControllerProvider {
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||
extension ProfileHeaderTableViewCell: MenuPreviewProvider {
|
||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
||||
let noteLabelPoint = noteLabel.convert(location, from: self)
|
||||
if noteLabel.bounds.contains(noteLabelPoint),
|
||||
let vc = noteLabel.getViewController(forLinkAt: noteLabelPoint) {
|
||||
return vc
|
||||
let link = noteLabel.getLink(atPoint: noteLabelPoint) {
|
||||
return (
|
||||
content: { self.noteLabel.getViewController(forLink: link.url, inRange: link.range) },
|
||||
actions: {
|
||||
let text = (self.noteLabel.text! as NSString).substring(with: link.range)
|
||||
if let mention = self.noteLabel.getMention(for: link.url, text: text) {
|
||||
return self.actionsForProfile(accountID: mention.id)
|
||||
} else if let hashtag = self.noteLabel.getHashtag(for: link.url, text: text) {
|
||||
return self.actionsForHashtag(hashtag)
|
||||
} else {
|
||||
return self.actionsForURL(link.url)
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
// TODO: should this also have peek/pop for avatar/header images?
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,20 +234,32 @@ extension ConversationMainStatusTableViewCell: AttachmentViewDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension ConversationMainStatusTableViewCell: PreviewViewControllerProvider {
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||
extension ConversationMainStatusTableViewCell: MenuPreviewProvider {
|
||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
||||
if avatarImageView.frame.contains(location) {
|
||||
return ProfileTableViewController(accountID: accountID)
|
||||
return (content: { ProfileTableViewController(accountID: self.accountID) }, actions: { self.actionsForProfile(accountID: self.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?.largeImage(image, description: description, sourceView: attachmentView)
|
||||
let description = attachmentView.attachment.description
|
||||
return (content: { self.delegate?.largeImage(image, description: description, sourceView: attachmentView) }, actions: { [] })
|
||||
}
|
||||
} else if contentLabel.frame.contains(location),
|
||||
let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) {
|
||||
return vc
|
||||
let link = contentLabel.getLink(atPoint: contentLabel.convert(location, from: self)) {
|
||||
return (
|
||||
content: { self.contentLabel.getViewController(forLink: link.url, inRange: link.range) },
|
||||
actions: {
|
||||
let text = (self.contentLabel.text! as NSString).substring(with: link.range)
|
||||
if let mention = self.contentLabel.getMention(for: link.url, text: text) {
|
||||
return self.actionsForProfile(accountID: mention.id)
|
||||
} else if let hashtag = self.contentLabel.getHashtag(for: link.url, text: text) {
|
||||
return self.actionsForHashtag(hashtag)
|
||||
} else {
|
||||
return self.actionsForURL(link.url)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -361,21 +361,35 @@ extension StatusTableViewCell: AttachmentViewDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension StatusTableViewCell: PreviewViewControllerProvider {
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||
extension StatusTableViewCell: MenuPreviewProvider {
|
||||
|
||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
||||
if avatarImageView.frame.contains(location) {
|
||||
return ProfileTableViewController(accountID: accountID)
|
||||
return (content: { ProfileTableViewController(accountID: self.accountID) }, actions: { self.actionsForProfile(accountID: self.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?.largeImage(image, description: description, sourceView: attachmentView)
|
||||
return (content: { self.delegate?.largeImage(image, description: description, sourceView: attachmentView) }, actions: { [] })
|
||||
}
|
||||
} else if contentLabel.frame.contains(location),
|
||||
let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) {
|
||||
return vc
|
||||
let link = contentLabel.getLink(atPoint: contentLabel.convert(location, from: self)) {
|
||||
return (
|
||||
content: { self.contentLabel.getViewController(forLink: link.url, inRange: link.range) },
|
||||
actions: {
|
||||
let text = (self.contentLabel.text! as NSString).substring(with: link.range)
|
||||
if let mention = self.contentLabel.getMention(for: link.url, text: text) {
|
||||
return self.actionsForProfile(accountID: mention.id)
|
||||
} else if let hashtag = self.contentLabel.getHashtag(for: link.url, text: text) {
|
||||
return self.actionsForHashtag(hashtag)
|
||||
} else {
|
||||
return self.actionsForURL(link.url)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
return ConversationTableViewController(for: statusID)
|
||||
return (content: { ConversationTableViewController(for: self.statusID) }, actions: { self.actionsForStatus(statusID: self.statusID) })
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue