Fix crash when tapping more actions buttons on iPad

Fixes #78
This commit is contained in:
Shadowfacts 2020-01-17 21:29:53 -05:00
parent 53702a8324
commit 8178a1f339
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
8 changed files with 48 additions and 35 deletions

View File

@ -261,7 +261,7 @@ extension ProfileTableViewController: StatusTableViewCellDelegate {
} }
extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate { extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
func showMoreOptions() { func showMoreOptions(cell: ProfileHeaderTableViewCell) {
let account = MastodonCache.account(for: accountID)! let account = MastodonCache.account(for: accountID)!
MastodonCache.relationship(for: account.id) { [weak self] (relationship) in MastodonCache.relationship(for: account.id) { [weak self] (relationship) in
@ -276,6 +276,7 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
DispatchQueue.main.async { DispatchQueue.main.async {
let activityController = UIActivityViewController(activityItems: [account.url, account], applicationActivities: customActivities) let activityController = UIActivityViewController(activityItems: [account.url, account], applicationActivities: customActivities)
activityController.completionWithItemsHandler = OpenInSafariActivity.completionHandler(viewController: self, url: account.url) activityController.completionWithItemsHandler = OpenInSafariActivity.completionHandler(viewController: self, url: account.url)
activityController.popoverPresentationController?.sourceView = cell.moreButtonVisualEffectView
self.present(activityController, animated: true) self.present(activityController, animated: true)
} }
} }

View File

@ -22,7 +22,7 @@ protocol MenuPreviewProvider {
extension MenuPreviewProvider { extension MenuPreviewProvider {
func actionsForProfile(accountID: String) -> [UIAction] { func actionsForProfile(accountID: String, sourceView: UIView?) -> [UIAction] {
guard let account = MastodonCache.account(for: accountID) else { return [] } guard let account = MastodonCache.account(for: accountID) else { return [] }
return [ return [
createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in
@ -32,27 +32,27 @@ extension MenuPreviewProvider {
self.navigationDelegate?.compose(mentioning: account.acct) self.navigationDelegate?.compose(mentioning: account.acct)
}), }),
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in 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 [ return [
createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in
self.navigationDelegate?.selected(url: url) self.navigationDelegate?.selected(url: url)
}), }),
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in 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] { func actionsForHashtag(_ hashtag: Hashtag, sourceView: UIView?) -> [UIAction] {
return actionsForURL(hashtag.url) 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 [] } guard let status = MastodonCache.status(for: statusID) else { return [] }
return [ return [
createAction(identifier: "reply", title: "Reply", systemImageName: "arrowshape.turn.up.left", handler: { (_) in createAction(identifier: "reply", title: "Reply", systemImageName: "arrowshape.turn.up.left", handler: { (_) in
@ -62,7 +62,7 @@ extension MenuPreviewProvider {
self.navigationDelegate?.selected(url: status.url!) self.navigationDelegate?.selected(url: status.url!)
}), }),
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in
self.navigationDelegate?.showMoreOptions(forStatus: statusID) self.navigationDelegate?.showMoreOptions(forStatus: statusID, sourceView: sourceView)
}) })
] ]
} }

View File

@ -44,11 +44,11 @@ protocol TuskerNavigationDelegate {
func showGallery(attachments: [Attachment], sourceViews: [UIImageView?], startIndex: Int) 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]) func showFollowedByList(accountIDs: [String])
@ -182,7 +182,7 @@ extension TuskerNavigationDelegate where Self: UIViewController {
present(gallery(attachments: attachments, sourceViews: sourceViews, startIndex: startIndex), animated: true) 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] = [ let customActivites: [UIActivity] = [
OpenInSafariActivity() OpenInSafariActivity()
] ]
@ -191,7 +191,7 @@ extension TuskerNavigationDelegate where Self: UIViewController {
return activityController 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 status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
guard let url = status.url else { fatalError("Missing url for status \(statusID)") } guard let url = status.url else { fatalError("Missing url for status \(statusID)") }
var customActivites: [UIActivity] = [OpenInSafariActivity()] var customActivites: [UIActivity] = [OpenInSafariActivity()]
@ -210,21 +210,27 @@ extension TuskerNavigationDelegate where Self: UIViewController {
return activityController 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)") } guard let account = MastodonCache.account(for: accountID) else { fatalError("Missing cached account \(accountID)") }
return moreOptions(forURL: account.url) return moreOptions(forURL: account.url)
} }
func showMoreOptions(forStatus statusID: String) { func showMoreOptions(forStatus statusID: String, sourceView: UIView?) {
present(moreOptions(forStatus: statusID), animated: true) let vc = moreOptions(forStatus: statusID)
vc.popoverPresentationController?.sourceView = sourceView
present(vc, animated: true)
} }
func showMoreOptions(forURL url: URL) { func showMoreOptions(forURL url: URL, sourceView: UIView?) {
present(moreOptions(forURL: url), animated: true) let vc = moreOptions(forURL: url)
vc.popoverPresentationController?.sourceView = sourceView
present(vc, animated: true)
} }
func showMoreOptions(forAccount accountID: String) { func showMoreOptions(forAccount accountID: String, sourceView: UIView?) {
present(moreOptions(forAccount: accountID), animated: true) let vc = moreOptions(forAccount: accountID)
vc.popoverPresentationController?.sourceView = sourceView
present(vc, animated: true)
} }
func showFollowedByList(accountIDs: [String]) { func showFollowedByList(accountIDs: [String]) {

View File

@ -68,6 +68,9 @@ extension AccountTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate } var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? { 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) }
)
} }
} }

View File

@ -209,7 +209,7 @@ class ContentLabel: LinkLabel {
} }
override func linkLongPressed(_ link: LinkLabel.Link) { override func linkLongPressed(_ link: LinkLabel.Link) {
navigationDelegate?.showMoreOptions(forURL: link.url) navigationDelegate?.showMoreOptions(forURL: link.url, sourceView: self)
} }
// MARK: - Navigation // MARK: - Navigation

View File

@ -10,7 +10,7 @@ import UIKit
import Pachyderm import Pachyderm
protocol ProfileHeaderTableViewCellDelegate: TuskerNavigationDelegate { protocol ProfileHeaderTableViewCellDelegate: TuskerNavigationDelegate {
func showMoreOptions() func showMoreOptions(cell: ProfileHeaderTableViewCell)
} }
class ProfileHeaderTableViewCell: UITableViewCell { class ProfileHeaderTableViewCell: UITableViewCell {
@ -137,7 +137,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
} }
@objc func morePressed() { @objc func morePressed() {
delegate?.showMoreOptions() delegate?.showMoreOptions(cell: self)
} }
@objc func avatarPressed() { @objc func avatarPressed() {
@ -161,11 +161,11 @@ extension ProfileHeaderTableViewCell: MenuPreviewProvider {
actions: { actions: {
let text = (self.noteLabel.text! as NSString).substring(with: link.range) let text = (self.noteLabel.text! as NSString).substring(with: link.range)
if let mention = self.noteLabel.getMention(for: link.url, text: text) { 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) { } else if let hashtag = self.noteLabel.getHashtag(for: link.url, text: text) {
return self.actionsForHashtag(hashtag) return self.actionsForHashtag(hashtag, sourceView: self)
} else { } else {
return self.actionsForURL(link.url) return self.actionsForURL(link.url, sourceView: self)
} }
} }
) )

View File

@ -289,7 +289,7 @@ class BaseStatusTableViewCell: UITableViewCell {
} }
@IBAction func morePressed() { @IBAction func morePressed() {
delegate?.showMoreOptions(forStatus: statusID) delegate?.showMoreOptions(forStatus: statusID, sourceView: moreButton)
} }
@objc func accountPressed() { @objc func accountPressed() {
@ -314,7 +314,10 @@ extension BaseStatusTableViewCell: MenuPreviewProvider {
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? { func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
if avatarImageView.frame.contains(location) { 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) { } else if attachmentsView.frame.contains(location) {
let attachmentsViewLocation = attachmentsView.convert(location, from: self) let attachmentsViewLocation = attachmentsView.convert(location, from: self)
if let attachmentView = attachmentsView.attachmentViews.allObjects.first(where: { $0.frame.contains(attachmentsViewLocation) }), if let attachmentView = attachmentsView.attachmentViews.allObjects.first(where: { $0.frame.contains(attachmentsViewLocation) }),
@ -329,11 +332,11 @@ extension BaseStatusTableViewCell: MenuPreviewProvider {
actions: { actions: {
let text = (self.contentLabel.text! as NSString).substring(with: link.range) let text = (self.contentLabel.text! as NSString).substring(with: link.range)
if let mention = self.contentLabel.getMention(for: link.url, text: text) { 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) { } else if let hashtag = self.contentLabel.getHashtag(for: link.url, text: text) {
return self.actionsForHashtag(hashtag) return self.actionsForHashtag(hashtag, sourceView: self)
} else { } else {
return self.actionsForURL(link.url) return self.actionsForURL(link.url, sourceView: self)
} }
} }
) )

View File

@ -127,7 +127,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
override func getStatusCellPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? { override func getStatusCellPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? {
return ( return (
content: { ConversationTableViewController(for: self.statusID, state: self.statusState.copy()) }, 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 reply.backgroundColor = tintColor
let more = UIContextualAction(style: .normal, title: "More") { (action, view, completion) in let more = UIContextualAction(style: .normal, title: "More") { (action, view, completion) in
completion(true) completion(true)
self.delegate?.showMoreOptions(forStatus: self.statusID) self.delegate?.showMoreOptions(forStatus: self.statusID, sourceView: self)
} }
more.image = UIImage(systemName: "ellipsis") more.image = UIImage(systemName: "ellipsis")
more.backgroundColor = .gray more.backgroundColor = .gray