191 lines
6.5 KiB
Swift
191 lines
6.5 KiB
Swift
//
|
|
// AttachmentView.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 8/31/18.
|
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import Pachyderm
|
|
import Gifu
|
|
import AVFoundation
|
|
|
|
protocol AttachmentViewDelegate: class {
|
|
func attachmentViewGallery(startingAt index: Int) -> UIViewController
|
|
func attachmentViewPresent(_ vc: UIViewController, animated: Bool)
|
|
}
|
|
|
|
class AttachmentView: UIImageView, GIFAnimatable {
|
|
|
|
weak var delegate: AttachmentViewDelegate?
|
|
|
|
var playImageView: UIImageView?
|
|
|
|
var attachment: Attachment!
|
|
var index: Int!
|
|
|
|
private var attachmentRequest: ImageCache.Request?
|
|
|
|
var gifData: Data?
|
|
|
|
public lazy var animator: Animator? = Animator(withDelegate: self)
|
|
|
|
init(attachment: Attachment, index: Int) {
|
|
super.init(image: nil)
|
|
commonInit()
|
|
|
|
self.attachment = attachment
|
|
self.index = index
|
|
loadAttachment()
|
|
}
|
|
|
|
deinit {
|
|
attachmentRequest?.cancel()
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
super.init(coder: aDecoder)
|
|
commonInit()
|
|
}
|
|
|
|
func commonInit() {
|
|
contentMode = .scaleAspectFill
|
|
layer.masksToBounds = true
|
|
isUserInteractionEnabled = true
|
|
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(imagePressed)))
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
|
|
|
addInteraction(UIContextMenuInteraction(delegate: self))
|
|
}
|
|
|
|
@objc func preferencesChanged() {
|
|
if let gifData = gifData {
|
|
if Preferences.shared.automaticallyPlayGifs && !isAnimatingGIF {
|
|
animate(withGIFData: gifData)
|
|
} else if !Preferences.shared.automaticallyPlayGifs && isAnimatingGIF {
|
|
stopAnimatingGIF()
|
|
}
|
|
}
|
|
}
|
|
|
|
func loadAttachment() {
|
|
guard AttachmentsContainerView.supportedAttachmentTypes.contains(attachment.kind) else {
|
|
preconditionFailure("invalid attachment type")
|
|
}
|
|
switch attachment.kind {
|
|
case .image:
|
|
loadImage()
|
|
case .video:
|
|
loadVideo()
|
|
case .audio:
|
|
loadAudio()
|
|
default:
|
|
preconditionFailure("invalid attachment type")
|
|
}
|
|
}
|
|
|
|
func loadImage() {
|
|
attachmentRequest = ImageCache.attachments.get(attachment.url) { [weak self] (data) in
|
|
guard let self = self, let data = data else { return }
|
|
self.attachmentRequest = nil
|
|
DispatchQueue.main.async {
|
|
if self.attachment.url.pathExtension == "gif" {
|
|
self.gifData = data
|
|
if Preferences.shared.automaticallyPlayGifs {
|
|
self.animate(withGIFData: data)
|
|
} else {
|
|
self.image = UIImage(data: data)
|
|
}
|
|
} else {
|
|
self.image = UIImage(data: data)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func loadVideo() {
|
|
let attachmentURL = self.attachment.url
|
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
let asset = AVURLAsset(url: attachmentURL)
|
|
let generator = AVAssetImageGenerator(asset: asset)
|
|
generator.appliesPreferredTrackTransform = true
|
|
guard let image = try? generator.copyCGImage(at: CMTime(seconds: 0, preferredTimescale: 1), actualTime: nil) else { return }
|
|
DispatchQueue.main.async {
|
|
guard self.attachment.url == attachmentURL else { return }
|
|
self.image = UIImage(cgImage: image)
|
|
}
|
|
}
|
|
|
|
let playImageView = UIImageView(image: UIImage(systemName: "play.circle.fill"))
|
|
playImageView.translatesAutoresizingMaskIntoConstraints = false
|
|
addSubview(playImageView)
|
|
NSLayoutConstraint.activate([
|
|
playImageView.widthAnchor.constraint(equalToConstant: 50),
|
|
playImageView.heightAnchor.constraint(equalToConstant: 50),
|
|
playImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
|
|
playImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
|
|
])
|
|
}
|
|
|
|
func loadAudio() {
|
|
let label = UILabel()
|
|
label.text = "Audio Only"
|
|
let playImageView = UIImageView(image: UIImage(systemName: "play.circle.fill"))
|
|
let stack = UIStackView(arrangedSubviews: [
|
|
label,
|
|
playImageView
|
|
])
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
stack.axis = .vertical
|
|
stack.spacing = 8
|
|
stack.alignment = .center
|
|
addSubview(stack)
|
|
NSLayoutConstraint.activate([
|
|
stack.centerXAnchor.constraint(equalTo: centerXAnchor),
|
|
stack.centerYAnchor.constraint(equalTo: centerYAnchor),
|
|
playImageView.widthAnchor.constraint(equalToConstant: 50),
|
|
playImageView.heightAnchor.constraint(equalToConstant: 50),
|
|
])
|
|
}
|
|
|
|
override func display(_ layer: CALayer) {
|
|
updateImageIfNeeded()
|
|
}
|
|
|
|
func showGallery() {
|
|
if let delegate = delegate {
|
|
let gallery = delegate.attachmentViewGallery(startingAt: index)
|
|
delegate.attachmentViewPresent(gallery, animated: true)
|
|
}
|
|
}
|
|
|
|
@objc func imagePressed() {
|
|
showGallery()
|
|
}
|
|
|
|
}
|
|
|
|
extension AttachmentView: UIContextMenuInteractionDelegate {
|
|
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
|
return UIContextMenuConfiguration(identifier: nil, previewProvider: { () -> UIViewController? in
|
|
if self.attachment.kind == .image {
|
|
return AttachmentPreviewViewController(attachment: self.attachment)
|
|
} else {
|
|
return self.delegate?.attachmentViewGallery(startingAt: self.index)
|
|
}
|
|
}, actionProvider: nil)
|
|
}
|
|
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
|
|
animator.addCompletion {
|
|
animator.preferredCommitStyle = .pop
|
|
if let gallery = animator.previewViewController as? GalleryViewController {
|
|
self.delegate?.attachmentViewPresent(gallery, animated: true)
|
|
} else {
|
|
self.showGallery()
|
|
}
|
|
}
|
|
}
|
|
}
|