mirror of
https://github.com/mastodon/mastodon-ios.git
synced 2025-01-28 16:20:10 +01:00
Add an ALT button to the media preview to display alt text
This commit is contained in:
parent
e8e15f3a0e
commit
582d1cf295
@ -99,6 +99,7 @@
|
||||
85904C02293BC0EB0011C817 /* ImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85904C01293BC0EB0011C817 /* ImageProvider.swift */; };
|
||||
85904C04293BC1940011C817 /* URLActivityItemWithMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85904C03293BC1940011C817 /* URLActivityItemWithMetadata.swift */; };
|
||||
85BC11B1292FF92C00E191CD /* HUDButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BC11B0292FF92C00E191CD /* HUDButton.swift */; };
|
||||
85BC11B32932414900E191CD /* AltViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BC11B22932414900E191CD /* AltViewController.swift */; };
|
||||
87FFDA5D898A5C42ADCB35E7 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */; };
|
||||
C24C97032922F30500BAE8CB /* RefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24C97022922F30500BAE8CB /* RefreshControl.swift */; };
|
||||
D87BFC8B291D5C6B00FEE264 /* MastodonLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */; };
|
||||
@ -622,6 +623,7 @@
|
||||
85904C01293BC0EB0011C817 /* ImageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProvider.swift; sourceTree = "<group>"; };
|
||||
85904C03293BC1940011C817 /* URLActivityItemWithMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLActivityItemWithMetadata.swift; sourceTree = "<group>"; };
|
||||
85BC11B0292FF92C00E191CD /* HUDButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDButton.swift; sourceTree = "<group>"; };
|
||||
85BC11B22932414900E191CD /* AltViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AltViewController.swift; sourceTree = "<group>"; };
|
||||
8850E70A1D5FF51432E43653 /* Pods-Mastodon-MastodonUITests.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.asdk - release.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.asdk - release.xcconfig"; sourceTree = "<group>"; };
|
||||
8E79CCBE51FBC3F7FE8CF49F /* Pods-MastodonTests.release snapshot.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.release snapshot.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.release snapshot.xcconfig"; sourceTree = "<group>"; };
|
||||
8ED8C4B1F1BA2DCFF2926BB1 /* Pods-Mastodon-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-NotificationService/Pods-Mastodon-NotificationService.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
@ -1978,6 +1980,7 @@
|
||||
DB6180E1263919780018D199 /* Paging */,
|
||||
85BC11B0292FF92C00E191CD /* HUDButton.swift */,
|
||||
DB6180DC263918E30018D199 /* MediaPreviewViewController.swift */,
|
||||
85BC11B22932414900E191CD /* AltViewController.swift */,
|
||||
DB6180F926391F2E0018D199 /* MediaPreviewViewModel.swift */,
|
||||
);
|
||||
path = MediaPreview;
|
||||
@ -3539,6 +3542,7 @@
|
||||
DB9D6BFF25E4F5940051B173 /* ProfileViewController.swift in Sources */,
|
||||
2D4AD8A226316CD200613EFC /* SelectedAccountSection.swift in Sources */,
|
||||
DB3EA8F1281B9EF600598866 /* DiscoveryCommunityViewModel+Diffable.swift in Sources */,
|
||||
85BC11B32932414900E191CD /* AltViewController.swift in Sources */,
|
||||
DB63F775279A997D00455B82 /* NotificationTableViewCell+ViewModel.swift in Sources */,
|
||||
DB98EB5927B109890082E365 /* ReportSupplementaryViewController.swift in Sources */,
|
||||
DB0617EB277EF3820030EE79 /* GradientBorderView.swift in Sources */,
|
||||
|
72
Mastodon/Scene/MediaPreview/AltViewController.swift
Normal file
72
Mastodon/Scene/MediaPreview/AltViewController.swift
Normal file
@ -0,0 +1,72 @@
|
||||
//
|
||||
// AltViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Jed Fox on 2022-11-26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
class AltViewController: UIViewController {
|
||||
var alt: String?
|
||||
let label = UILabel()
|
||||
|
||||
convenience init(alt: String?, sourceView: UIView?) {
|
||||
self.init(nibName: nil, bundle: nil)
|
||||
self.alt = alt
|
||||
self.modalPresentationStyle = .popover
|
||||
self.popoverPresentationController?.delegate = self
|
||||
self.popoverPresentationController?.permittedArrowDirections = .up
|
||||
self.popoverPresentationController?.sourceView = sourceView
|
||||
self.overrideUserInterfaceStyle = .dark
|
||||
}
|
||||
|
||||
@objc override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
|
||||
}
|
||||
|
||||
@MainActor required dynamic init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.lineBreakStrategy = .standard
|
||||
label.font = .preferredFont(forTextStyle: .callout)
|
||||
label.text = alt ?? "ummmmmmm tbd but you shouldn’t see this"
|
||||
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(label)
|
||||
|
||||
NSLayoutConstraint.activate(
|
||||
NSLayoutConstraint.constraints(withVisualFormat: "V:|-[label]-|", metrics: nil, views: ["label": label])
|
||||
)
|
||||
NSLayoutConstraint.activate(
|
||||
NSLayoutConstraint.constraints(withVisualFormat: "H:|-[label]-|", metrics: nil, views: ["label": label])
|
||||
)
|
||||
NSLayoutConstraint.activate([
|
||||
label.widthAnchor.constraint(lessThanOrEqualToConstant: 400),
|
||||
])
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
UIView.performWithoutAnimation {
|
||||
preferredContentSize = CGSize(
|
||||
width: label.intrinsicContentSize.width + view.layoutMargins.left + view.layoutMargins.right,
|
||||
height: label.intrinsicContentSize.height + view.layoutMargins.top + view.layoutMargins.bottom
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UIPopoverPresentationControllerDelegate
|
||||
extension AltViewController: UIPopoverPresentationControllerDelegate {
|
||||
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
||||
.none
|
||||
}
|
||||
}
|
@ -25,7 +25,9 @@ class HUDButton: UIView {
|
||||
let button: UIButton = {
|
||||
let button = HighlightDimmableButton()
|
||||
button.expandEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
|
||||
button.contentEdgeInsets = .constant(7)
|
||||
button.imageView?.tintColor = .label
|
||||
button.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .bold))
|
||||
return button
|
||||
}()
|
||||
|
||||
@ -56,4 +58,9 @@ class HUDButton: UIView {
|
||||
heightAnchor.constraint(equalToConstant: HUDButton.height).priority(.defaultHigh),
|
||||
])
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
button.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .bold))
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +110,10 @@ extension MediaPreviewImageViewController: MediaPreviewPage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var altText: String? {
|
||||
viewModel.item.altText
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ImageAnalysisInteractionDelegate
|
||||
|
@ -29,6 +29,10 @@ final class MediaPreviewViewController: UIViewController, NeedsDependency {
|
||||
button.setImage(UIImage(systemName: "xmark", withConfiguration: UIImage.SymbolConfiguration(pointSize: 16, weight: .bold))!, for: .normal)
|
||||
}
|
||||
|
||||
let altButton = HUDButton { button in
|
||||
button.setTitle("ALT", for: .normal)
|
||||
}
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
@ -58,7 +62,13 @@ extension MediaPreviewViewController {
|
||||
closeButton.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
|
||||
closeButton.widthAnchor.constraint(equalToConstant: HUDButton.height).priority(.defaultHigh),
|
||||
])
|
||||
|
||||
|
||||
view.addSubview(altButton)
|
||||
NSLayoutConstraint.activate([
|
||||
altButton.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 12),
|
||||
altButton.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
|
||||
])
|
||||
|
||||
viewModel.mediaPreviewImageViewControllerDelegate = self
|
||||
|
||||
pagingViewController.interPageSpacing = 10
|
||||
@ -66,7 +76,8 @@ extension MediaPreviewViewController {
|
||||
pagingViewController.dataSource = viewModel
|
||||
|
||||
closeButton.button.addTarget(self, action: #selector(MediaPreviewViewController.closeButtonPressed(_:)), for: .touchUpInside)
|
||||
|
||||
altButton.button.addTarget(self, action: #selector(MediaPreviewViewController.altButtonPressed(_:)), for: .touchUpInside)
|
||||
|
||||
// bind view model
|
||||
viewModel.$currentPage
|
||||
.receive(on: DispatchQueue.main)
|
||||
@ -144,7 +155,12 @@ extension MediaPreviewViewController {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@objc private func altButtonPressed(_ sender: UIButton) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
present(AltViewController(alt: viewModel.viewControllers[viewModel.currentPage].altText, sourceView: sender), animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - MediaPreviewingViewController
|
||||
|
@ -14,6 +14,7 @@ import MastodonCore
|
||||
|
||||
protocol MediaPreviewPage: UIViewController {
|
||||
func setShowingChrome(_ showingChrome: Bool)
|
||||
var altText: String? { get }
|
||||
}
|
||||
|
||||
final class MediaPreviewViewModel: NSObject {
|
||||
@ -27,9 +28,9 @@ final class MediaPreviewViewModel: NSObject {
|
||||
|
||||
@Published var currentPage: Int
|
||||
@Published var showingChrome = true
|
||||
|
||||
|
||||
// output
|
||||
let viewControllers: [UIViewController]
|
||||
let viewControllers: [MediaPreviewPage]
|
||||
|
||||
private var disposeBag: Set<AnyCancellable> = []
|
||||
|
||||
@ -65,7 +66,8 @@ final class MediaPreviewViewModel: NSObject {
|
||||
context: context,
|
||||
item: .gif(.init(
|
||||
assetURL: attachment.assetURL.flatMap { URL(string: $0) },
|
||||
previewURL: attachment.previewURL.flatMap { URL(string: $0) }
|
||||
previewURL: attachment.previewURL.flatMap { URL(string: $0) },
|
||||
altText: attachment.altDescription
|
||||
))
|
||||
)
|
||||
viewController.viewModel = viewModel
|
||||
@ -76,7 +78,8 @@ final class MediaPreviewViewModel: NSObject {
|
||||
context: context,
|
||||
item: .video(.init(
|
||||
assetURL: attachment.assetURL.flatMap { URL(string: $0) },
|
||||
previewURL: attachment.previewURL.flatMap { URL(string: $0) }
|
||||
previewURL: attachment.previewURL.flatMap { URL(string: $0) },
|
||||
altText: attachment.altDescription
|
||||
))
|
||||
)
|
||||
viewController.viewModel = viewModel
|
||||
|
@ -111,6 +111,10 @@ extension MediaPreviewVideoViewController: MediaPreviewPage {
|
||||
func setShowingChrome(_ showingChrome: Bool) {
|
||||
// TODO: does this do anything?
|
||||
}
|
||||
|
||||
var altText: String? {
|
||||
viewModel.item.altText
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AVPlayerViewControllerDelegate
|
||||
|
@ -125,17 +125,26 @@ extension MediaPreviewVideoViewModel {
|
||||
case .gif(let mediaContext): return mediaContext.assetURL
|
||||
}
|
||||
}
|
||||
|
||||
var altText: String? {
|
||||
switch self {
|
||||
case .video(let mediaContext): return mediaContext.altText
|
||||
case .gif(let mediaContext): return mediaContext.altText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RemoteVideoContext {
|
||||
let assetURL: URL?
|
||||
let previewURL: URL?
|
||||
let altText: String?
|
||||
// let thumbnail: UIImage?
|
||||
}
|
||||
|
||||
struct RemoteGIFContext {
|
||||
let assetURL: URL?
|
||||
let previewURL: URL?
|
||||
let altText: String?
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user