Move GIFImageView to TuskerComponents

This commit is contained in:
Shadowfacts 2023-04-16 13:17:39 -04:00
parent bb3f353dbc
commit 350e331eb2
9 changed files with 64 additions and 17 deletions

View File

@ -1,6 +1,6 @@
// //
// GIFImageView.swift // GIFImageView.swift
// Tusker // TuskerComponents
// //
// Created by Shadowfacts on 11/11/21. // Created by Shadowfacts on 11/11/21.
// Copyright © 2021 Shadowfacts. All rights reserved. // Copyright © 2021 Shadowfacts. All rights reserved.
@ -8,36 +8,36 @@
import UIKit import UIKit
class GIFImageView: UIImageView { open class GIFImageView: UIImageView {
fileprivate(set) var gifController: GIFController? = nil public fileprivate(set) var gifController: GIFController? = nil
var isAnimatingGIF: Bool { gifController?.state == .playing } public var isAnimatingGIF: Bool { gifController?.state == .playing }
/// Detaches the current GIF controller from this view. /// Detaches the current GIF controller from this view.
/// If this view is the GIF controller's only one, it will stop itself. /// If this view is the GIF controller's only one, it will stop itself.
func detachGIFController() { public func detachGIFController() {
gifController?.detach(from: self) gifController?.detach(from: self)
} }
} }
/// A `GIFController` controls the animation of one or more `GIFImageView`s. /// A `GIFController` controls the animation of one or more `GIFImageView`s.
class GIFController { public class GIFController {
// GIFImageView strongly holds the controller so that when the last view detaches, the controller is freed // GIFImageView strongly holds the controller so that when the last view detaches, the controller is freed
private var imageViews = WeakArray<GIFImageView>() private var imageViews = WeakArray<GIFImageView>()
private(set) var gifData: Data public let gifData: Data
private(set) var state: State = .stopped private(set) var state: State = .stopped
private(set) var lastFrame: (image: UIImage, index: Int)? = nil public private(set) var lastFrame: (image: UIImage, index: Int)? = nil
init(gifData: Data) { public init(gifData: Data) {
self.gifData = gifData self.gifData = gifData
} }
/// Attaches another view to this controller, letting it play back alongside the others. /// Attaches another view to this controller, letting it play back alongside the others.
/// Immediately brings it into sync with the others, setting the last frame if there was one. /// Immediately brings it into sync with the others, setting the last frame if there was one.
func attach(to view: GIFImageView) { public func attach(to view: GIFImageView) {
imageViews.append(view) imageViews.append(view)
view.gifController = self view.gifController = self
@ -49,13 +49,13 @@ class GIFController {
/// Detaches the given view from this controller. /// Detaches the given view from this controller.
/// If no views attached views remain, the last strong reference to this controller is nilled out /// If no views attached views remain, the last strong reference to this controller is nilled out
/// and image animation will stop at the next CGAnimateImageDataWithBlock callback. /// and image animation will stop at the next CGAnimateImageDataWithBlock callback.
func detach(from view: GIFImageView) { public func detach(from view: GIFImageView) {
// todo: does === work the way i want here // todo: does === work the way i want here
imageViews.removeAll(where: { $0 === view }) imageViews.removeAll(where: { $0 === view })
view.gifController = nil view.gifController = nil
} }
func startAnimating() { public func startAnimating() {
guard state.shouldStop else { return } guard state.shouldStop else { return }
state = .playing state = .playing
@ -74,7 +74,7 @@ class GIFController {
} }
} }
func stopAnimating() { public func stopAnimating() {
guard state == .playing else { return } guard state == .playing else { return }
state = .stopping state = .stopping
@ -89,3 +89,47 @@ class GIFController {
} }
} }
private class WeakHolder<T: AnyObject> {
weak var object: T?
init(_ object: T?) {
self.object = object
}
}
private struct WeakArray<Element: AnyObject>: MutableCollection, RangeReplaceableCollection {
private var array: [WeakHolder<Element>]
var startIndex: Int { array.startIndex }
var endIndex: Int { array.endIndex }
init() {
array = []
}
init(_ elements: [Element]) {
array = elements.map { WeakHolder($0) }
}
init(_ elements: [Element?]) {
array = elements.map { WeakHolder($0) }
}
subscript(position: Int) -> Element? {
get {
array[position].object
}
set(newValue) {
array[position] = WeakHolder(newValue)
}
}
func index(after i: Int) -> Int {
return array.index(after: i)
}
mutating func replaceSubrange<C>(_ subrange: Range<Int>, with newElements: C) where C : Collection, Self.Element == C.Element {
array.replaceSubrange(subrange, with: newElements.map { WeakHolder($0) })
}
}

View File

@ -332,7 +332,6 @@
D6D9498D298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498C298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift */; }; D6D9498D298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498C298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift */; };
D6D9498F298EB79400C59229 /* CopyableLable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498E298EB79400C59229 /* CopyableLable.swift */; }; D6D9498F298EB79400C59229 /* CopyableLable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498E298EB79400C59229 /* CopyableLable.swift */; };
D6DD2A3F273C1F4900386A6C /* ComposeAttachmentImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */; }; D6DD2A3F273C1F4900386A6C /* ComposeAttachmentImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */; };
D6DD2A45273D6C5700386A6C /* GIFImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A44273D6C5700386A6C /* GIFImageView.swift */; };
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; }; D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; };
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */; }; D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */; };
D6DD8FFD298495A8002AD3FD /* LogoutService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD8FFC298495A8002AD3FD /* LogoutService.swift */; }; D6DD8FFD298495A8002AD3FD /* LogoutService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD8FFC298495A8002AD3FD /* LogoutService.swift */; };
@ -758,7 +757,6 @@
D6D9498C298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingStatusCollectionViewCell.swift; sourceTree = "<group>"; }; D6D9498C298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingStatusCollectionViewCell.swift; sourceTree = "<group>"; };
D6D9498E298EB79400C59229 /* CopyableLable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableLable.swift; sourceTree = "<group>"; }; D6D9498E298EB79400C59229 /* CopyableLable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableLable.swift; sourceTree = "<group>"; };
D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAttachmentImage.swift; sourceTree = "<group>"; }; D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAttachmentImage.swift; sourceTree = "<group>"; };
D6DD2A44273D6C5700386A6C /* GIFImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GIFImageView.swift; sourceTree = "<group>"; };
D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = "<group>"; }; D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = "<group>"; };
D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+Notification.swift"; sourceTree = "<group>"; }; D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+Notification.swift"; sourceTree = "<group>"; };
D6DD8FFC298495A8002AD3FD /* LogoutService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogoutService.swift; sourceTree = "<group>"; }; D6DD8FFC298495A8002AD3FD /* LogoutService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogoutService.swift; sourceTree = "<group>"; };
@ -1459,7 +1457,6 @@
D61F75B8293C15A000C0B37F /* ZeroHeightCollectionViewCell.swift */, D61F75B8293C15A000C0B37F /* ZeroHeightCollectionViewCell.swift */,
D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */, D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */,
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */, D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */,
D6DD2A44273D6C5700386A6C /* GIFImageView.swift */,
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */, D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */,
D620483323D3801D008A63EF /* LinkTextView.swift */, D620483323D3801D008A63EF /* LinkTextView.swift */,
D61A45E728DF477D002BE511 /* LoadingCollectionViewCell.swift */, D61A45E728DF477D002BE511 /* LoadingCollectionViewCell.swift */,
@ -2145,7 +2142,6 @@
D6C143DA253510F4007DC240 /* ComposeEmojiTextField.swift in Sources */, D6C143DA253510F4007DC240 /* ComposeEmojiTextField.swift in Sources */,
D6DD2A3F273C1F4900386A6C /* ComposeAttachmentImage.swift in Sources */, D6DD2A3F273C1F4900386A6C /* ComposeAttachmentImage.swift in Sources */,
D65B4B6229771A3F00DABDFB /* FetchStatusService.swift in Sources */, D65B4B6229771A3F00DABDFB /* FetchStatusService.swift in Sources */,
D6DD2A45273D6C5700386A6C /* GIFImageView.swift in Sources */,
D61F759029353B4300C0B37F /* FileManager+Size.swift in Sources */, D61F759029353B4300C0B37F /* FileManager+Size.swift in Sources */,
0427033822B30F5F000D31B6 /* BehaviorPrefsView.swift in Sources */, 0427033822B30F5F000D31B6 /* BehaviorPrefsView.swift in Sources */,
D6945C2F23AC47C3005C403C /* SavedDataManager.swift in Sources */, D6945C2F23AC47C3005C403C /* SavedDataManager.swift in Sources */,

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import TuskerComponents
class AttachmentPreviewViewController: UIViewController { class AttachmentPreviewViewController: UIViewController {

View File

@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
import Photos import Photos
import TuskerComponents
struct ComposeAttachmentImage: View { struct ComposeAttachmentImage: View {
let attachment: CompositionAttachment let attachment: CompositionAttachment

View File

@ -10,6 +10,7 @@ import UIKit
import Pachyderm import Pachyderm
@preconcurrency import AVFoundation @preconcurrency import AVFoundation
@preconcurrency import VisionKit @preconcurrency import VisionKit
import TuskerComponents
protocol LargeImageContentView: UIView { protocol LargeImageContentView: UIView {
var animationImage: UIImage? { get } var animationImage: UIImage? { get }

View File

@ -7,6 +7,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import TuskerComponents
class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableViewController { class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableViewController {

View File

@ -7,6 +7,7 @@
// //
import UIKit import UIKit
import TuskerComponents
protocol LargeImageAnimatableViewController: UIViewController { protocol LargeImageAnimatableViewController: UIViewController {
var animationSourceView: UIImageView? { get } var animationSourceView: UIImageView? { get }

View File

@ -7,6 +7,7 @@
// //
import UIKit import UIKit
import TuskerComponents
class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTransitioning { class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

View File

@ -9,6 +9,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import AVFoundation import AVFoundation
import TuskerComponents
protocol AttachmentViewDelegate: AnyObject { protocol AttachmentViewDelegate: AnyObject {
func attachmentViewGallery(startingAt index: Int) -> GalleryViewController? func attachmentViewGallery(startingAt index: Int) -> GalleryViewController?