Compare commits
6 Commits
cf71fc3f98
...
3ecee61013
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 3ecee61013 | |
Shadowfacts | f9aee46bbe | |
Shadowfacts | 1cf3ce48ce | |
Shadowfacts | 072bb0daf0 | |
Shadowfacts | d36e0ad27d | |
Shadowfacts | a80cbe79c2 |
|
@ -15,6 +15,8 @@ public protocol GalleryContentViewController: UIViewController {
|
||||||
var caption: String? { get }
|
var caption: String? { get }
|
||||||
var bottomControlsAccessoryViewController: UIViewController? { get }
|
var bottomControlsAccessoryViewController: UIViewController? { get }
|
||||||
var canAnimateFromSourceView: Bool { get }
|
var canAnimateFromSourceView: Bool { get }
|
||||||
|
|
||||||
|
func setControlsVisible(_ visible: Bool, animated: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension GalleryContentViewController {
|
public extension GalleryContentViewController {
|
||||||
|
@ -25,4 +27,7 @@ public extension GalleryContentViewController {
|
||||||
var canAnimateFromSourceView: Bool {
|
var canAnimateFromSourceView: Bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setControlsVisible(_ visible: Bool, animated: Bool) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import Foundation
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public protocol GalleryContentViewControllerContainer {
|
public protocol GalleryContentViewControllerContainer {
|
||||||
|
var galleryControlsVisible: Bool { get }
|
||||||
|
|
||||||
func setGalleryContentLoading(_ loading: Bool)
|
func setGalleryContentLoading(_ loading: Bool)
|
||||||
func galleryContentChanged()
|
func galleryContentChanged()
|
||||||
func disableGalleryScrollAndZoom()
|
func disableGalleryScrollAndZoom()
|
||||||
|
|
|
@ -192,6 +192,8 @@ class GalleryItemViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addContent() {
|
func addContent() {
|
||||||
|
content.setControlsVisible(controlsVisible, animated: false)
|
||||||
|
|
||||||
content.view.translatesAutoresizingMaskIntoConstraints = false
|
content.view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
if content.parent != self {
|
if content.parent != self {
|
||||||
addChild(content)
|
addChild(content)
|
||||||
|
@ -229,6 +231,7 @@ class GalleryItemViewController: UIViewController {
|
||||||
func updateControlsViews() {
|
func updateControlsViews() {
|
||||||
topControlsView.transform = CGAffineTransform(translationX: 0, y: visible ? 0 : -topControlsView.bounds.height)
|
topControlsView.transform = CGAffineTransform(translationX: 0, y: visible ? 0 : -topControlsView.bounds.height)
|
||||||
bottomControlsView.transform = CGAffineTransform(translationX: 0, y: visible ? 0 : bottomControlsView.bounds.height)
|
bottomControlsView.transform = CGAffineTransform(translationX: 0, y: visible ? 0 : bottomControlsView.bounds.height)
|
||||||
|
content.setControlsVisible(visible, animated: animated)
|
||||||
}
|
}
|
||||||
if animated {
|
if animated {
|
||||||
let animator = UIViewPropertyAnimator(duration: 0.2, timingParameters: UISpringTimingParameters())
|
let animator = UIViewPropertyAnimator(duration: 0.2, timingParameters: UISpringTimingParameters())
|
||||||
|
@ -396,6 +399,10 @@ class GalleryItemViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GalleryItemViewController: GalleryContentViewControllerContainer {
|
extension GalleryItemViewController: GalleryContentViewControllerContainer {
|
||||||
|
var galleryControlsVisible: Bool {
|
||||||
|
controlsVisible
|
||||||
|
}
|
||||||
|
|
||||||
func setGalleryContentLoading(_ loading: Bool) {
|
func setGalleryContentLoading(_ loading: Bool) {
|
||||||
if loading {
|
if loading {
|
||||||
if activityIndicator == nil {
|
if activityIndicator == nil {
|
||||||
|
|
|
@ -195,7 +195,7 @@
|
||||||
D6934F3A2BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F392BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift */; };
|
D6934F3A2BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F392BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift */; };
|
||||||
D6934F3C2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F3B2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift */; };
|
D6934F3C2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F3B2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift */; };
|
||||||
D6934F3E2BAA19D5002B1C8D /* ImageActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F3D2BAA19D5002B1C8D /* ImageActivityItemSource.swift */; };
|
D6934F3E2BAA19D5002B1C8D /* ImageActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F3D2BAA19D5002B1C8D /* ImageActivityItemSource.swift */; };
|
||||||
D6934F402BAA19EC002B1C8D /* GifvActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F3F2BAA19EC002B1C8D /* GifvActivityItemSource.swift */; };
|
D6934F402BAA19EC002B1C8D /* VideoActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F3F2BAA19EC002B1C8D /* VideoActivityItemSource.swift */; };
|
||||||
D693A72825CF282E003A14E2 /* TrendingHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */; };
|
D693A72825CF282E003A14E2 /* TrendingHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */; };
|
||||||
D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */; };
|
D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */; };
|
||||||
D693A73025CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */; };
|
D693A73025CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */; };
|
||||||
|
@ -596,7 +596,7 @@
|
||||||
D6934F392BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FallbackGalleryContentViewController.swift; sourceTree = "<group>"; };
|
D6934F392BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FallbackGalleryContentViewController.swift; sourceTree = "<group>"; };
|
||||||
D6934F3B2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoGalleryContentViewController.swift; sourceTree = "<group>"; };
|
D6934F3B2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoGalleryContentViewController.swift; sourceTree = "<group>"; };
|
||||||
D6934F3D2BAA19D5002B1C8D /* ImageActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageActivityItemSource.swift; sourceTree = "<group>"; };
|
D6934F3D2BAA19D5002B1C8D /* ImageActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageActivityItemSource.swift; sourceTree = "<group>"; };
|
||||||
D6934F3F2BAA19EC002B1C8D /* GifvActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvActivityItemSource.swift; sourceTree = "<group>"; };
|
D6934F3F2BAA19EC002B1C8D /* VideoActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoActivityItemSource.swift; sourceTree = "<group>"; };
|
||||||
D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagsViewController.swift; sourceTree = "<group>"; };
|
D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagsViewController.swift; sourceTree = "<group>"; };
|
||||||
D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedProfileCollectionViewCell.swift; sourceTree = "<group>"; };
|
D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedProfileCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FeaturedProfileCollectionViewCell.xib; sourceTree = "<group>"; };
|
D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FeaturedProfileCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||||
|
@ -1348,7 +1348,7 @@
|
||||||
D681E4D6246E32290053414F /* StatusActivityItemSource.swift */,
|
D681E4D6246E32290053414F /* StatusActivityItemSource.swift */,
|
||||||
D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */,
|
D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */,
|
||||||
D6934F3D2BAA19D5002B1C8D /* ImageActivityItemSource.swift */,
|
D6934F3D2BAA19D5002B1C8D /* ImageActivityItemSource.swift */,
|
||||||
D6934F3F2BAA19EC002B1C8D /* GifvActivityItemSource.swift */,
|
D6934F3F2BAA19EC002B1C8D /* VideoActivityItemSource.swift */,
|
||||||
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */,
|
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */,
|
||||||
D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */,
|
D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */,
|
||||||
D6CA8CDD296387310050C433 /* SaveToPhotosActivity.swift */,
|
D6CA8CDD296387310050C433 /* SaveToPhotosActivity.swift */,
|
||||||
|
@ -2211,7 +2211,7 @@
|
||||||
D6E426B325337C7000C02E1C /* CustomEmojiImageView.swift in Sources */,
|
D6E426B325337C7000C02E1C /* CustomEmojiImageView.swift in Sources */,
|
||||||
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */,
|
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */,
|
||||||
D61F75B3293BD89C00C0B37F /* UpdateFilterService.swift in Sources */,
|
D61F75B3293BD89C00C0B37F /* UpdateFilterService.swift in Sources */,
|
||||||
D6934F402BAA19EC002B1C8D /* GifvActivityItemSource.swift in Sources */,
|
D6934F402BAA19EC002B1C8D /* VideoActivityItemSource.swift in Sources */,
|
||||||
D6C3F5172991C1A00009FCFF /* View+AppListStyle.swift in Sources */,
|
D6C3F5172991C1A00009FCFF /* View+AppListStyle.swift in Sources */,
|
||||||
D65B4B6A297777D900DABDFB /* StatusNotFoundView.swift in Sources */,
|
D65B4B6A297777D900DABDFB /* StatusNotFoundView.swift in Sources */,
|
||||||
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
//
|
|
||||||
// GifvActivityItemSource.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 3/19/24.
|
|
||||||
// Copyright © 2024 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import UniformTypeIdentifiers
|
|
||||||
import AVFoundation
|
|
||||||
|
|
||||||
class GifvActivityItemSource: NSObject, UIActivityItemSource {
|
|
||||||
let asset: AVAsset
|
|
||||||
let url: URL
|
|
||||||
|
|
||||||
init(asset: AVAsset, url: URL) {
|
|
||||||
self.asset = asset
|
|
||||||
self.url = url
|
|
||||||
}
|
|
||||||
|
|
||||||
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? {
|
|
||||||
#if os(visionOS)
|
|
||||||
#warning("Use async AVAssetImageGenerator.image(at:)")
|
|
||||||
return nil
|
|
||||||
#else
|
|
||||||
let generator = AVAssetImageGenerator(asset: self.asset)
|
|
||||||
generator.appliesPreferredTrackTransform = true
|
|
||||||
if let image = try? generator.copyCGImage(at: .zero, actualTime: nil) {
|
|
||||||
return UIImage(cgImage: image)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
|
|
||||||
do {
|
|
||||||
let data = try Data(contentsOf: url)
|
|
||||||
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent)
|
|
||||||
try data.write(to: tempURL)
|
|
||||||
return tempURL
|
|
||||||
} catch {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String {
|
|
||||||
return (UTType(filenameExtension: url.pathExtension) ?? .video).identifier
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,7 +23,14 @@ class SaveToPhotosActivity: UIActivity {
|
||||||
return "Save to Photos"
|
return "Save to Photos"
|
||||||
}
|
}
|
||||||
override var activityImage: UIImage? {
|
override var activityImage: UIImage? {
|
||||||
UIImage(systemName: "square.and.arrow.down")
|
// Just using the symbol image directly causes it to be stretched.
|
||||||
|
let symbol = UIImage(systemName: "square.and.arrow.down", withConfiguration: UIImage.SymbolConfiguration(scale: .large))!
|
||||||
|
let format = UIGraphicsImageRendererFormat()
|
||||||
|
format.scale = UIScreen.main.scale
|
||||||
|
return UIGraphicsImageRenderer(size: CGSize(width: 76, height: 76), format: format).image { ctx in
|
||||||
|
let rect = AVMakeRect(aspectRatio: symbol.size, insideRect: CGRect(x: 0, y: 0, width: 76, height: 76))
|
||||||
|
symbol.draw(in: rect)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
|
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// VideoActivityItemSource.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 3/19/24.
|
||||||
|
// Copyright © 2024 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
|
class VideoActivityItemSource: UIActivityItemProvider {
|
||||||
|
private let asset: AVAsset
|
||||||
|
private let url: URL
|
||||||
|
|
||||||
|
private var tempURL: URL?
|
||||||
|
|
||||||
|
init(asset: AVAsset, url: URL) {
|
||||||
|
self.asset = asset
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
super.init(placeholderItem: url)
|
||||||
|
}
|
||||||
|
|
||||||
|
override var item: Any {
|
||||||
|
if let tempURL {
|
||||||
|
return tempURL
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
let data = try Data(contentsOf: url)
|
||||||
|
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent)
|
||||||
|
try data.write(to: tempURL)
|
||||||
|
self.tempURL = tempURL
|
||||||
|
return tempURL
|
||||||
|
} catch {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String {
|
||||||
|
return (UTType(filenameExtension: url.pathExtension) ?? .video).identifier
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,13 +11,15 @@ import GalleryVC
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
class GifvGalleryContentViewController: UIViewController, GalleryContentViewController {
|
class GifvGalleryContentViewController: UIViewController, GalleryContentViewController {
|
||||||
let controller: GifvController
|
private let controller: GifvController
|
||||||
|
private let url: URL
|
||||||
let caption: String?
|
let caption: String?
|
||||||
|
|
||||||
private var presentationSizeCancellable: AnyCancellable?
|
private var presentationSizeCancellable: AnyCancellable?
|
||||||
|
|
||||||
init(controller: GifvController, caption: String?) {
|
init(controller: GifvController, url: URL, caption: String?) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
|
self.url = url
|
||||||
self.caption = caption
|
self.caption = caption
|
||||||
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
@ -64,8 +66,7 @@ class GifvGalleryContentViewController: UIViewController, GalleryContentViewCont
|
||||||
}
|
}
|
||||||
|
|
||||||
var activityItemsForSharing: [Any] {
|
var activityItemsForSharing: [Any] {
|
||||||
// TODO: share gifv
|
[VideoActivityItemSource(asset: controller.item.asset, url: url)]
|
||||||
[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import UIKit
|
||||||
import GalleryVC
|
import GalleryVC
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import TuskerComponents
|
import TuskerComponents
|
||||||
|
@preconcurrency import VisionKit
|
||||||
|
|
||||||
class ImageGalleryContentViewController: UIViewController, GalleryContentViewController {
|
class ImageGalleryContentViewController: UIViewController, GalleryContentViewController {
|
||||||
let url: URL
|
let url: URL
|
||||||
|
@ -18,6 +19,16 @@ class ImageGalleryContentViewController: UIViewController, GalleryContentViewCon
|
||||||
let image: UIImage
|
let image: UIImage
|
||||||
let gifController: GIFController?
|
let gifController: GIFController?
|
||||||
|
|
||||||
|
private var imageView: GIFImageView!
|
||||||
|
|
||||||
|
@available(iOS 16.0, macCatalyst 17.0, *)
|
||||||
|
private static let analyzer = ImageAnalyzer()
|
||||||
|
private var _analysisInteraction: AnyObject?
|
||||||
|
@available(iOS 16.0, macCatalyst 17.0, *)
|
||||||
|
private var analysisInteraction: ImageAnalysisInteraction? { _analysisInteraction as? ImageAnalysisInteraction }
|
||||||
|
|
||||||
|
private var isGrayscale = false
|
||||||
|
|
||||||
init(url: URL, caption: String?, originalData: Data?, image: UIImage, gifController: GIFController?) {
|
init(url: URL, caption: String?, originalData: Data?, image: UIImage, gifController: GIFController?) {
|
||||||
self.url = url
|
self.url = url
|
||||||
self.caption = caption
|
self.caption = caption
|
||||||
|
@ -37,9 +48,17 @@ class ImageGalleryContentViewController: UIViewController, GalleryContentViewCon
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
let imageView = GIFImageView(image: image)
|
isGrayscale = Preferences.shared.grayscaleImages
|
||||||
|
let maybeGrayscaleImage = if isGrayscale {
|
||||||
|
ImageGrayscalifier.convert(url: url, image: image) ?? image
|
||||||
|
} else {
|
||||||
|
image
|
||||||
|
}
|
||||||
|
|
||||||
|
imageView = GIFImageView(image: maybeGrayscaleImage)
|
||||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
imageView.contentMode = .scaleAspectFit
|
imageView.contentMode = .scaleAspectFit
|
||||||
|
imageView.isUserInteractionEnabled = true
|
||||||
view.addSubview(imageView)
|
view.addSubview(imageView)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||||
|
@ -51,6 +70,24 @@ class ImageGalleryContentViewController: UIViewController, GalleryContentViewCon
|
||||||
if let gifController {
|
if let gifController {
|
||||||
gifController.attach(to: imageView)
|
gifController.attach(to: imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if gifController == nil,
|
||||||
|
#available(iOS 16.0, macCatalyst 17.0, *) {
|
||||||
|
let interaction = ImageAnalysisInteraction(self)
|
||||||
|
self._analysisInteraction = interaction
|
||||||
|
interaction.preferredInteractionTypes = .automatic
|
||||||
|
imageView.addInteraction(interaction)
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
let result = try await ImageGalleryContentViewController.analyzer.analyze(image, configuration: ImageAnalyzer.Configuration([.text, .machineReadableCode]))
|
||||||
|
interaction.analysis = result
|
||||||
|
} catch {
|
||||||
|
// if analysis fails, we just don't show anything
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
@ -61,6 +98,20 @@ class ImageGalleryContentViewController: UIViewController, GalleryContentViewCon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func preferencesChanged() {
|
||||||
|
if isGrayscale != Preferences.shared.grayscaleImages {
|
||||||
|
isGrayscale = Preferences.shared.grayscaleImages
|
||||||
|
let image = if isGrayscale {
|
||||||
|
ImageGrayscalifier.convert(url: url, image: image)
|
||||||
|
} else {
|
||||||
|
image
|
||||||
|
}
|
||||||
|
if let image {
|
||||||
|
imageView.image = image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: GalleryContentViewController
|
// MARK: GalleryContentViewController
|
||||||
|
|
||||||
var container: (any GalleryVC.GalleryContentViewControllerContainer)?
|
var container: (any GalleryVC.GalleryContentViewControllerContainer)?
|
||||||
|
@ -76,4 +127,18 @@ class ImageGalleryContentViewController: UIViewController, GalleryContentViewCon
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setControlsVisible(_ visible: Bool, animated: Bool) {
|
||||||
|
if #available(iOS 16.0, macCatalyst 17.0, *),
|
||||||
|
let analysisInteraction {
|
||||||
|
analysisInteraction.setSupplementaryInterfaceHidden(!visible, animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 16.0, macCatalyst 17.0, *)
|
||||||
|
extension ImageGalleryContentViewController: ImageAnalysisInteractionDelegate {
|
||||||
|
func interaction(_ interaction: ImageAnalysisInteraction, shouldBeginAt point: CGPoint, for interactionType: ImageAnalysisInteraction.InteractionTypes) -> Bool {
|
||||||
|
return container?.galleryControlsVisible ?? true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ class LoadingGalleryContentViewController: UIViewController, GalleryContentViewC
|
||||||
if let wrapped = await provider() {
|
if let wrapped = await provider() {
|
||||||
self.wrapped = wrapped
|
self.wrapped = wrapped
|
||||||
wrapped.container = container
|
wrapped.container = container
|
||||||
|
wrapped.setControlsVisible(container?.galleryControlsVisible ?? false, animated: false)
|
||||||
|
|
||||||
addChild(wrapped)
|
addChild(wrapped)
|
||||||
wrapped.view.translatesAutoresizingMaskIntoConstraints = false
|
wrapped.view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
@ -99,4 +100,8 @@ class LoadingGalleryContentViewController: UIViewController, GalleryContentViewC
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setControlsVisible(_ visible: Bool, animated: Bool) {
|
||||||
|
wrapped?.setControlsVisible(visible, animated: animated)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ class StatusAttachmentsGalleryDataSource: GalleryDataSource {
|
||||||
} else {
|
} else {
|
||||||
GifvController(asset: AVAsset(url: attachment.url))
|
GifvController(asset: AVAsset(url: attachment.url))
|
||||||
}
|
}
|
||||||
return GifvGalleryContentViewController(controller: controller, caption: attachment.description)
|
return GifvGalleryContentViewController(controller: controller, url: attachment.url, caption: attachment.description)
|
||||||
case .video:
|
case .video:
|
||||||
return VideoGalleryContentViewController(url: attachment.url, caption: attachment.description)
|
return VideoGalleryContentViewController(url: attachment.url, caption: attachment.description)
|
||||||
case .audio:
|
case .audio:
|
||||||
|
|
|
@ -92,8 +92,7 @@ class VideoGalleryContentViewController: UIViewController, GalleryContentViewCon
|
||||||
}
|
}
|
||||||
|
|
||||||
var activityItemsForSharing: [Any] {
|
var activityItemsForSharing: [Any] {
|
||||||
// TODO: share videos
|
[VideoActivityItemSource(asset: item.asset, url: url)]
|
||||||
[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,7 +448,7 @@ extension AttachmentView: UIContextMenuInteractionDelegate {
|
||||||
return ImageGalleryContentViewController(url: self.attachment.url, caption: nil, originalData: nil, image: image, gifController: self.gifController)
|
return ImageGalleryContentViewController(url: self.attachment.url, caption: nil, originalData: nil, image: image, gifController: self.gifController)
|
||||||
} else if self.attachment.kind == .gifv,
|
} else if self.attachment.kind == .gifv,
|
||||||
let gifvView {
|
let gifvView {
|
||||||
return GifvGalleryContentViewController(controller: gifvView.controller, caption: nil)
|
return GifvGalleryContentViewController(controller: gifvView.controller, url: self.attachment.url, caption: nil)
|
||||||
} else if self.attachment.kind == .video || self.attachment.kind == .audio {
|
} else if self.attachment.kind == .video || self.attachment.kind == .audio {
|
||||||
let vc = VideoGalleryContentViewController(url: self.attachment.url, caption: nil)
|
let vc = VideoGalleryContentViewController(url: self.attachment.url, caption: nil)
|
||||||
vc.player.isMuted = true
|
vc.player.isMuted = true
|
||||||
|
@ -478,8 +478,8 @@ extension AttachmentView: UIContextMenuInteractionDelegate {
|
||||||
itemSource = ImageActivityItemSource(data: data, url: url, image: image)
|
itemSource = ImageActivityItemSource(data: data, url: url, image: image)
|
||||||
itemData = Task { data }
|
itemData = Task { data }
|
||||||
}
|
}
|
||||||
} else if self.attachment.kind == .gifv {
|
} else if self.attachment.kind == .gifv || self.attachment.kind == .video {
|
||||||
itemSource = GifvActivityItemSource(asset: AVAsset(url: self.attachment.url), url: self.attachment.url)
|
itemSource = VideoActivityItemSource(asset: AVAsset(url: self.attachment.url), url: self.attachment.url)
|
||||||
itemData = Task {
|
itemData = Task {
|
||||||
try? await URLSession.shared.data(from: self.attachment.url).0
|
try? await URLSession.shared.data(from: self.attachment.url).0
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ class GifvController {
|
||||||
|
|
||||||
init(asset: AVAsset) {
|
init(asset: AVAsset) {
|
||||||
self.asset = asset
|
self.asset = asset
|
||||||
self.item = AVPlayerItem(asset: asset)
|
self.item = GifvController.createItem(asset: asset)
|
||||||
self.player = AVPlayer(playerItem: item)
|
self.player = AVPlayer(playerItem: item)
|
||||||
|
|
||||||
self.isGrayscale = Preferences.shared.grayscaleImages
|
self.isGrayscale = Preferences.shared.grayscaleImages
|
||||||
|
|
Loading…
Reference in New Issue