Tusker/Tusker/Views/Attachments/AttachmentsContainerView.swift

320 lines
12 KiB
Swift

//
// AttachmentsContainerView.swift
// Tusker
//
// Created by Shadowfacts on 6/16/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import UIKit
import Pachyderm
class AttachmentsContainerView: UIView {
var delegate: AttachmentViewDelegate?
var statusID: String!
var attachments: [Attachment]!
let attachmentViews: NSHashTable<AttachmentView> = .weakObjects()
var blurView: UIVisualEffectView?
var hideButton: UIButton?
var contentHidden: Bool! {
didSet {
guard let blurView = blurView,
let hideButton = hideButton else { return }
blurView.alpha = contentHidden ? 0 : 1
hideButton.alpha = contentHidden ? 1 : 0
UIView.animate(withDuration: 0.2) {
blurView.alpha = self.contentHidden ? 1 : 0
hideButton.alpha = self.contentHidden ? 0 : 1
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
self.isUserInteractionEnabled = true
}
func getAttachmentView(for attachment: Attachment) -> AttachmentView? {
return attachmentViews.allObjects.first { $0.attachment.id == attachment.id }
}
// MARK: - User Interaface
func updateUI(status: Status) {
self.statusID = status.id
attachments = status.attachments.filter { $0.kind == .image || $0.kind == .video }
attachmentViews.removeAllObjects()
subviews.forEach { $0.removeFromSuperview() }
if attachments.count > 0 {
self.isHidden = false
var accessibilityElements = [Any]()
switch attachments.count {
case 1:
let attachmentView = createAttachmentView(index: 0)
fillView(attachmentView)
accessibilityElements.append(attachmentView)
case 2:
let left = createAttachmentView(index: 0)
let right = createAttachmentView(index: 1)
fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left,
right
]))
NSLayoutConstraint.activate([
left.halfWidth()
])
accessibilityElements.append(left)
accessibilityElements.append(right)
case 3:
let left = createAttachmentView(index: 0)
let topRight = createAttachmentView(index: 1)
let bottomRight = createAttachmentView(index: 2)
fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left,
createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topRight,
bottomRight
])
]))
NSLayoutConstraint.activate([
left.halfWidth(),
topRight.halfHeight(),
])
accessibilityElements.append(left)
accessibilityElements.append(topRight)
accessibilityElements.append(bottomRight)
case 4:
let topLeft = createAttachmentView(index: 0)
let bottomLeft = createAttachmentView(index: 2)
let left = createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topLeft,
bottomLeft
])
let topRight = createAttachmentView(index: 1)
let bottomRight = createAttachmentView(index: 3)
fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left,
createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topRight,
bottomRight
])
]))
NSLayoutConstraint.activate([
left.halfWidth(),
topLeft.halfHeight(),
topRight.halfHeight(),
])
accessibilityElements.append(topLeft)
accessibilityElements.append(topRight)
accessibilityElements.append(bottomLeft)
accessibilityElements.append(bottomRight)
default: // more than 4
let moreView = UIView()
moreView.backgroundColor = .secondarySystemBackground
moreView.translatesAutoresizingMaskIntoConstraints = false
moreView.isUserInteractionEnabled = true
moreView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(moreViewTapped)))
let moreLabel = UILabel()
moreLabel.text = "\(attachments.count - 3) more..."
moreLabel.textColor = .secondaryLabel
moreLabel.textAlignment = .center
moreLabel.translatesAutoresizingMaskIntoConstraints = false
moreView.addSubview(moreLabel)
moreView.accessibilityLabel = moreLabel.text
let topLeft = createAttachmentView(index: 0)
let bottomLeft = createAttachmentView(index: 2)
let left = createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topLeft,
bottomLeft
])
let topRight = createAttachmentView(index: 1)
fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left,
createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topRight,
moreView
])
]))
NSLayoutConstraint.activate([
left.halfWidth(),
topLeft.halfHeight(),
topRight.halfHeight(),
moreView.leadingAnchor.constraint(equalTo: moreLabel.leadingAnchor),
moreLabel.trailingAnchor.constraint(equalTo: moreView.trailingAnchor),
moreView.topAnchor.constraint(equalTo: moreLabel.topAnchor),
moreLabel.bottomAnchor.constraint(equalTo: moreView.bottomAnchor),
])
accessibilityElements.append(topLeft)
accessibilityElements.append(topRight)
accessibilityElements.append(bottomLeft)
accessibilityElements.append(moreView)
}
self.accessibilityElements = accessibilityElements
} else {
self.isHidden = true
}
if status.sensitive {
contentHidden = true
createBlurView()
createHideButton()
}
}
private func createAttachmentView(index: Int) -> AttachmentView {
let attachmentView = AttachmentView(attachment: attachments[index], index: index)
attachmentView.delegate = delegate
attachmentView.translatesAutoresizingMaskIntoConstraints = false
attachmentView.isAccessibilityElement = true
attachmentView.accessibilityTraits = [.image, .button]
attachmentView.accessibilityLabel = String(format: NSLocalizedString("Attachment %d", comment: "attachment at index accessiblity label"), index + 1)
attachmentViews.add(attachmentView)
return attachmentView
}
private func createAttachmentsStack(axis: NSLayoutConstraint.Axis, arrangedSubviews: [UIView]) -> UIStackView {
let stack = UIStackView(arrangedSubviews: arrangedSubviews)
stack.axis = axis
stack.spacing = 4
stack.translatesAutoresizingMaskIntoConstraints = false
return stack
}
private func createBlurView() {
let blur = UIBlurEffect(style: .dark)
let blurView = UIVisualEffectView(effect: blur)
blurView.effect = blur
blurView.translatesAutoresizingMaskIntoConstraints = false
fillView(blurView)
let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blur, style: .label))
vibrancyView.translatesAutoresizingMaskIntoConstraints = false
fillView(vibrancyView, in: blurView.contentView)
blurView.contentView.addSubview(vibrancyView)
let image = UIImage(systemName: "eye")!
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.text = "Sensitive Content"
let stack = UIStackView(arrangedSubviews: [
imageView,
label
])
stack.axis = .vertical
stack.alignment = .center
stack.translatesAutoresizingMaskIntoConstraints = false
vibrancyView.contentView.addSubview(stack)
NSLayoutConstraint.activate([
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: image.size.width / image.size.height),
imageView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.2),
stack.centerXAnchor.constraint(equalTo: centerXAnchor),
stack.centerYAnchor.constraint(equalTo: centerYAnchor),
stack.widthAnchor.constraint(equalTo: widthAnchor)
])
self.blurView = blurView
blurView.isUserInteractionEnabled = true
blurView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(blurViewTapped)))
}
private func createHideButton() {
let hideButton = UIButton()
hideButton.translatesAutoresizingMaskIntoConstraints = false
hideButton.alpha = 0
hideButton.layer.cornerRadius = 2
hideButton.layer.masksToBounds = true
hideButton.setImage(UIImage(systemName: "eye.slash.fill"), for: .normal)
hideButton.addTarget(self, action: #selector(hideButtonTapped), for: .touchUpInside)
addSubview(hideButton)
NSLayoutConstraint.activate([
hideButton.topAnchor.constraint(equalTo: topAnchor, constant: 8),
hideButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8)
])
self.hideButton = hideButton
}
private func fillView(_ view: UIView, in parentView: UIView? = nil) {
let parentView = parentView ?? self
parentView.addSubview(view)
NSLayoutConstraint.activate([
view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor),
view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor),
view.topAnchor.constraint(equalTo: parentView.topAnchor),
view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor)
])
}
// MARK: - Interaction
@objc func blurViewTapped() {
contentHidden = false
}
@objc func hideButtonTapped() {
contentHidden = true
}
@objc func showSensitiveContent() {
guard let blurView = blurView else { return }
blurView.alpha = 1
UIView.animate(withDuration: 0.2) {
blurView.alpha = 0
}
}
@objc func hideSensitiveContent() {
guard let blurView = self.blurView else { return }
blurView.alpha = 0
UIView.animate(withDuration: 0.2) {
blurView.alpha = 1
}
}
@objc func moreViewTapped() {
guard attachments.count > 4 else { return }
// the more view shows up in place of the fourth attachemtn view, show tapping it should start at the fourth attachment
delegate?.showAttachmentsGallery(startingAt: 3)
}
}
fileprivate extension UIView {
enum RelativeSize {
case full, half
var multiplier: CGFloat {
switch self {
case .full:
return 1
case .half:
return 0.5
}
}
}
func halfWidth(spacing: CGFloat = 4) -> NSLayoutConstraint {
return widthAnchor.constraint(equalTo: superview!.widthAnchor, multiplier: 0.5, constant: -spacing / 2)
}
func halfHeight(spacing: CGFloat = 4) -> NSLayoutConstraint {
return heightAnchor.constraint(equalTo: superview!.heightAnchor, multiplier: 0.5, constant: -spacing / 2)
}
}