From 8178a1f339fbd24e1437a7435115778326b0fbae Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 17 Jan 2020 21:29:53 -0500 Subject: [PATCH] Fix crash when tapping more actions buttons on iPad Fixes #78 --- .../Profile/ProfileTableViewController.swift | 3 +- Tusker/Screens/Utilities/Previewing.swift | 16 +++++----- Tusker/TuskerNavigationDelegate.swift | 30 +++++++++++-------- .../Account Cell/AccountTableViewCell.swift | 5 +++- Tusker/Views/ContentLabel.swift | 2 +- .../ProfileHeaderTableViewCell.swift | 10 +++---- .../Status/BaseStatusTableViewCell.swift | 13 ++++---- .../Status/TimelineStatusTableViewCell.swift | 4 +-- 8 files changed, 48 insertions(+), 35 deletions(-) diff --git a/Tusker/Screens/Profile/ProfileTableViewController.swift b/Tusker/Screens/Profile/ProfileTableViewController.swift index 2dc0100b..3386dcab 100644 --- a/Tusker/Screens/Profile/ProfileTableViewController.swift +++ b/Tusker/Screens/Profile/ProfileTableViewController.swift @@ -261,7 +261,7 @@ extension ProfileTableViewController: StatusTableViewCellDelegate { } extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate { - func showMoreOptions() { + func showMoreOptions(cell: ProfileHeaderTableViewCell) { let account = MastodonCache.account(for: accountID)! MastodonCache.relationship(for: account.id) { [weak self] (relationship) in @@ -276,6 +276,7 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate { DispatchQueue.main.async { let activityController = UIActivityViewController(activityItems: [account.url, account], applicationActivities: customActivities) activityController.completionWithItemsHandler = OpenInSafariActivity.completionHandler(viewController: self, url: account.url) + activityController.popoverPresentationController?.sourceView = cell.moreButtonVisualEffectView self.present(activityController, animated: true) } } diff --git a/Tusker/Screens/Utilities/Previewing.swift b/Tusker/Screens/Utilities/Previewing.swift index 2c20e4e7..46a3793f 100644 --- a/Tusker/Screens/Utilities/Previewing.swift +++ b/Tusker/Screens/Utilities/Previewing.swift @@ -22,7 +22,7 @@ protocol MenuPreviewProvider { extension MenuPreviewProvider { - func actionsForProfile(accountID: String) -> [UIAction] { + func actionsForProfile(accountID: String, sourceView: UIView?) -> [UIAction] { guard let account = MastodonCache.account(for: accountID) else { return [] } return [ createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in @@ -32,27 +32,27 @@ extension MenuPreviewProvider { self.navigationDelegate?.compose(mentioning: account.acct) }), createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in - self.navigationDelegate?.showMoreOptions(forAccount: accountID) + self.navigationDelegate?.showMoreOptions(forAccount: accountID, sourceView: sourceView) }) ] } - func actionsForURL(_ url: URL) -> [UIAction] { + func actionsForURL(_ url: URL, sourceView: UIView?) -> [UIAction] { return [ createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in self.navigationDelegate?.selected(url: url) }), createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in - self.navigationDelegate?.showMoreOptions(forURL: url) + self.navigationDelegate?.showMoreOptions(forURL: url, sourceView: sourceView) }) ] } - func actionsForHashtag(_ hashtag: Hashtag) -> [UIAction] { - return actionsForURL(hashtag.url) + func actionsForHashtag(_ hashtag: Hashtag, sourceView: UIView?) -> [UIAction] { + return actionsForURL(hashtag.url, sourceView: sourceView) } - func actionsForStatus(statusID: String) -> [UIAction] { + func actionsForStatus(statusID: String, sourceView: UIView?) -> [UIAction] { guard let status = MastodonCache.status(for: statusID) else { return [] } return [ createAction(identifier: "reply", title: "Reply", systemImageName: "arrowshape.turn.up.left", handler: { (_) in @@ -62,7 +62,7 @@ extension MenuPreviewProvider { self.navigationDelegate?.selected(url: status.url!) }), createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in - self.navigationDelegate?.showMoreOptions(forStatus: statusID) + self.navigationDelegate?.showMoreOptions(forStatus: statusID, sourceView: sourceView) }) ] } diff --git a/Tusker/TuskerNavigationDelegate.swift b/Tusker/TuskerNavigationDelegate.swift index a62072b2..9f342077 100644 --- a/Tusker/TuskerNavigationDelegate.swift +++ b/Tusker/TuskerNavigationDelegate.swift @@ -44,11 +44,11 @@ protocol TuskerNavigationDelegate { func showGallery(attachments: [Attachment], sourceViews: [UIImageView?], startIndex: Int) - func showMoreOptions(forStatus statusID: String) + func showMoreOptions(forStatus statusID: String, sourceView: UIView?) - func showMoreOptions(forAccount accountID: String) + func showMoreOptions(forAccount accountID: String, sourceView: UIView?) - func showMoreOptions(forURL url: URL) + func showMoreOptions(forURL url: URL, sourceView: UIView?) func showFollowedByList(accountIDs: [String]) @@ -182,7 +182,7 @@ extension TuskerNavigationDelegate where Self: UIViewController { present(gallery(attachments: attachments, sourceViews: sourceViews, startIndex: startIndex), animated: true) } - private func moreOptions(forURL url: URL) -> UIViewController { + private func moreOptions(forURL url: URL) -> UIActivityViewController { let customActivites: [UIActivity] = [ OpenInSafariActivity() ] @@ -191,7 +191,7 @@ extension TuskerNavigationDelegate where Self: UIViewController { return activityController } - private func moreOptions(forStatus statusID: String) -> UIViewController { + private func moreOptions(forStatus statusID: String) -> UIActivityViewController { guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") } guard let url = status.url else { fatalError("Missing url for status \(statusID)") } var customActivites: [UIActivity] = [OpenInSafariActivity()] @@ -210,21 +210,27 @@ extension TuskerNavigationDelegate where Self: UIViewController { return activityController } - private func moreOptions(forAccount accountID: String) -> UIViewController { + private func moreOptions(forAccount accountID: String) -> UIActivityViewController { guard let account = MastodonCache.account(for: accountID) else { fatalError("Missing cached account \(accountID)") } return moreOptions(forURL: account.url) } - func showMoreOptions(forStatus statusID: String) { - present(moreOptions(forStatus: statusID), animated: true) + func showMoreOptions(forStatus statusID: String, sourceView: UIView?) { + let vc = moreOptions(forStatus: statusID) + vc.popoverPresentationController?.sourceView = sourceView + present(vc, animated: true) } - func showMoreOptions(forURL url: URL) { - present(moreOptions(forURL: url), animated: true) + func showMoreOptions(forURL url: URL, sourceView: UIView?) { + let vc = moreOptions(forURL: url) + vc.popoverPresentationController?.sourceView = sourceView + present(vc, animated: true) } - func showMoreOptions(forAccount accountID: String) { - present(moreOptions(forAccount: accountID), animated: true) + func showMoreOptions(forAccount accountID: String, sourceView: UIView?) { + let vc = moreOptions(forAccount: accountID) + vc.popoverPresentationController?.sourceView = sourceView + present(vc, animated: true) } func showFollowedByList(accountIDs: [String]) { diff --git a/Tusker/Views/Account Cell/AccountTableViewCell.swift b/Tusker/Views/Account Cell/AccountTableViewCell.swift index e933d335..9dd1bc02 100644 --- a/Tusker/Views/Account Cell/AccountTableViewCell.swift +++ b/Tusker/Views/Account Cell/AccountTableViewCell.swift @@ -68,6 +68,9 @@ extension AccountTableViewCell: MenuPreviewProvider { var navigationDelegate: TuskerNavigationDelegate? { return delegate } func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? { - return (content: { ProfileTableViewController(accountID: self.accountID) }, actions: { self.actionsForProfile(accountID: self.accountID) }) + return ( + content: { ProfileTableViewController(accountID: self.accountID) }, + actions: { self.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView) } + ) } } diff --git a/Tusker/Views/ContentLabel.swift b/Tusker/Views/ContentLabel.swift index 1cdced23..0a965d06 100644 --- a/Tusker/Views/ContentLabel.swift +++ b/Tusker/Views/ContentLabel.swift @@ -209,7 +209,7 @@ class ContentLabel: LinkLabel { } override func linkLongPressed(_ link: LinkLabel.Link) { - navigationDelegate?.showMoreOptions(forURL: link.url) + navigationDelegate?.showMoreOptions(forURL: link.url, sourceView: self) } // MARK: - Navigation diff --git a/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift b/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift index a3517792..be52ae50 100644 --- a/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift +++ b/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift @@ -10,7 +10,7 @@ import UIKit import Pachyderm protocol ProfileHeaderTableViewCellDelegate: TuskerNavigationDelegate { - func showMoreOptions() + func showMoreOptions(cell: ProfileHeaderTableViewCell) } class ProfileHeaderTableViewCell: UITableViewCell { @@ -137,7 +137,7 @@ class ProfileHeaderTableViewCell: UITableViewCell { } @objc func morePressed() { - delegate?.showMoreOptions() + delegate?.showMoreOptions(cell: self) } @objc func avatarPressed() { @@ -161,11 +161,11 @@ extension ProfileHeaderTableViewCell: MenuPreviewProvider { 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) + return self.actionsForProfile(accountID: mention.id, sourceView: self) } else if let hashtag = self.noteLabel.getHashtag(for: link.url, text: text) { - return self.actionsForHashtag(hashtag) + return self.actionsForHashtag(hashtag, sourceView: self) } else { - return self.actionsForURL(link.url) + return self.actionsForURL(link.url, sourceView: self) } } ) diff --git a/Tusker/Views/Status/BaseStatusTableViewCell.swift b/Tusker/Views/Status/BaseStatusTableViewCell.swift index f17fe0ac..5e5bfe38 100644 --- a/Tusker/Views/Status/BaseStatusTableViewCell.swift +++ b/Tusker/Views/Status/BaseStatusTableViewCell.swift @@ -289,7 +289,7 @@ class BaseStatusTableViewCell: UITableViewCell { } @IBAction func morePressed() { - delegate?.showMoreOptions(forStatus: statusID) + delegate?.showMoreOptions(forStatus: statusID, sourceView: moreButton) } @objc func accountPressed() { @@ -314,7 +314,10 @@ extension BaseStatusTableViewCell: MenuPreviewProvider { func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? { if avatarImageView.frame.contains(location) { - return (content: { ProfileTableViewController(accountID: self.accountID)}, actions: { self.actionsForProfile(accountID: self.accountID) }) + return ( + content: { ProfileTableViewController(accountID: self.accountID)}, + actions: { self.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView) } + ) } else if attachmentsView.frame.contains(location) { let attachmentsViewLocation = attachmentsView.convert(location, from: self) if let attachmentView = attachmentsView.attachmentViews.allObjects.first(where: { $0.frame.contains(attachmentsViewLocation) }), @@ -329,11 +332,11 @@ extension BaseStatusTableViewCell: MenuPreviewProvider { 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) + return self.actionsForProfile(accountID: mention.id, sourceView: self) } else if let hashtag = self.contentLabel.getHashtag(for: link.url, text: text) { - return self.actionsForHashtag(hashtag) + return self.actionsForHashtag(hashtag, sourceView: self) } else { - return self.actionsForURL(link.url) + return self.actionsForURL(link.url, sourceView: self) } } ) diff --git a/Tusker/Views/Status/TimelineStatusTableViewCell.swift b/Tusker/Views/Status/TimelineStatusTableViewCell.swift index 15be357b..0ce1aab8 100644 --- a/Tusker/Views/Status/TimelineStatusTableViewCell.swift +++ b/Tusker/Views/Status/TimelineStatusTableViewCell.swift @@ -127,7 +127,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell { override func getStatusCellPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? { return ( content: { ConversationTableViewController(for: self.statusID, state: self.statusState.copy()) }, - actions: { self.actionsForStatus(statusID: self.statusID) } + actions: { self.actionsForStatus(statusID: self.statusID, sourceView: self) } ) } @@ -211,7 +211,7 @@ extension TimelineStatusTableViewCell: TableViewSwipeActionProvider { reply.backgroundColor = tintColor let more = UIContextualAction(style: .normal, title: "More") { (action, view, completion) in completion(true) - self.delegate?.showMoreOptions(forStatus: self.statusID) + self.delegate?.showMoreOptions(forStatus: self.statusID, sourceView: self) } more.image = UIImage(systemName: "ellipsis") more.backgroundColor = .gray