Move GIFImageView to TuskerComponents
This commit is contained in:
parent
bb3f353dbc
commit
350e331eb2
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// GIFImageView.swift
|
||||
// Tusker
|
||||
// TuskerComponents
|
||||
//
|
||||
// Created by Shadowfacts on 11/11/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
|
@ -8,36 +8,36 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
class GIFImageView: UIImageView {
|
||||
open class GIFImageView: UIImageView {
|
||||
|
||||
fileprivate(set) var gifController: GIFController? = nil
|
||||
var isAnimatingGIF: Bool { gifController?.state == .playing }
|
||||
public fileprivate(set) var gifController: GIFController? = nil
|
||||
public var isAnimatingGIF: Bool { gifController?.state == .playing }
|
||||
|
||||
/// Detaches the current GIF controller from this view.
|
||||
/// If this view is the GIF controller's only one, it will stop itself.
|
||||
func detachGIFController() {
|
||||
public func detachGIFController() {
|
||||
gifController?.detach(from: self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// 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
|
||||
private var imageViews = WeakArray<GIFImageView>()
|
||||
|
||||
private(set) var gifData: Data
|
||||
public let gifData: Data
|
||||
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
|
||||
}
|
||||
|
||||
/// 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.
|
||||
func attach(to view: GIFImageView) {
|
||||
public func attach(to view: GIFImageView) {
|
||||
imageViews.append(view)
|
||||
view.gifController = self
|
||||
|
||||
|
@ -49,13 +49,13 @@ class GIFController {
|
|||
/// Detaches the given view from this controller.
|
||||
/// 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.
|
||||
func detach(from view: GIFImageView) {
|
||||
public func detach(from view: GIFImageView) {
|
||||
// todo: does === work the way i want here
|
||||
imageViews.removeAll(where: { $0 === view })
|
||||
view.gifController = nil
|
||||
}
|
||||
|
||||
func startAnimating() {
|
||||
public func startAnimating() {
|
||||
guard state.shouldStop else { return }
|
||||
|
||||
state = .playing
|
||||
|
@ -74,7 +74,7 @@ class GIFController {
|
|||
}
|
||||
}
|
||||
|
||||
func stopAnimating() {
|
||||
public func stopAnimating() {
|
||||
guard state == .playing else { return }
|
||||
|
||||
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) })
|
||||
}
|
||||
}
|
|
@ -332,7 +332,6 @@
|
|||
D6D9498D298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498C298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift */; };
|
||||
D6D9498F298EB79400C59229 /* CopyableLable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498E298EB79400C59229 /* CopyableLable.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 */; };
|
||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353E22F502EC00A9563A /* Preferences+Notification.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -1459,7 +1457,6 @@
|
|||
D61F75B8293C15A000C0B37F /* ZeroHeightCollectionViewCell.swift */,
|
||||
D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */,
|
||||
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */,
|
||||
D6DD2A44273D6C5700386A6C /* GIFImageView.swift */,
|
||||
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */,
|
||||
D620483323D3801D008A63EF /* LinkTextView.swift */,
|
||||
D61A45E728DF477D002BE511 /* LoadingCollectionViewCell.swift */,
|
||||
|
@ -2145,7 +2142,6 @@
|
|||
D6C143DA253510F4007DC240 /* ComposeEmojiTextField.swift in Sources */,
|
||||
D6DD2A3F273C1F4900386A6C /* ComposeAttachmentImage.swift in Sources */,
|
||||
D65B4B6229771A3F00DABDFB /* FetchStatusService.swift in Sources */,
|
||||
D6DD2A45273D6C5700386A6C /* GIFImageView.swift in Sources */,
|
||||
D61F759029353B4300C0B37F /* FileManager+Size.swift in Sources */,
|
||||
0427033822B30F5F000D31B6 /* BehaviorPrefsView.swift in Sources */,
|
||||
D6945C2F23AC47C3005C403C /* SavedDataManager.swift in Sources */,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import TuskerComponents
|
||||
|
||||
class AttachmentPreviewViewController: UIViewController {
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import SwiftUI
|
||||
import Photos
|
||||
import TuskerComponents
|
||||
|
||||
struct ComposeAttachmentImage: View {
|
||||
let attachment: CompositionAttachment
|
||||
|
|
|
@ -10,6 +10,7 @@ import UIKit
|
|||
import Pachyderm
|
||||
@preconcurrency import AVFoundation
|
||||
@preconcurrency import VisionKit
|
||||
import TuskerComponents
|
||||
|
||||
protocol LargeImageContentView: UIView {
|
||||
var animationImage: UIImage? { get }
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import TuskerComponents
|
||||
|
||||
class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableViewController {
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import TuskerComponents
|
||||
|
||||
protocol LargeImageAnimatableViewController: UIViewController {
|
||||
var animationSourceView: UIImageView? { get }
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import TuskerComponents
|
||||
|
||||
class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import UIKit
|
||||
import Pachyderm
|
||||
import AVFoundation
|
||||
import TuskerComponents
|
||||
|
||||
protocol AttachmentViewDelegate: AnyObject {
|
||||
func attachmentViewGallery(startingAt index: Int) -> GalleryViewController?
|
||||
|
|
Loading…
Reference in New Issue