diff --git a/Source/Pages/Gallery/YPAssetViewContainer.swift b/Source/Pages/Gallery/YPAssetViewContainer.swift
index 9c57902f0..4f12cc393 100644
--- a/Source/Pages/Gallery/YPAssetViewContainer.swift
+++ b/Source/Pages/Gallery/YPAssetViewContainer.swift
@@ -12,13 +12,17 @@ import Stevia
import AVFoundation
/// The container for asset (video or image). It containts the YPGridView and YPAssetZoomableView.
-class YPAssetViewContainer: UIView {
- public var zoomableView: YPAssetZoomableView?
+final class YPAssetViewContainer: UIView {
+ public var zoomableView: YPAssetZoomableView
public var itemOverlay: UIView?
public let curtain = UIView()
public let spinnerView = UIView()
public let squareCropButton = UIButton()
- public let multipleSelectionButton = UIButton()
+ public let multipleSelectionButton: UIButton = {
+ let v = UIButton()
+ v.setImage(YPConfig.icons.multipleSelectionOffIcon, for: .normal)
+ return v
+ }()
public var onlySquare = YPConfig.library.onlySquare
public var isShown = true
public var spinnerIsShown = false
@@ -28,97 +32,96 @@ class YPAssetViewContainer: UIView {
private var isMultipleSelection = false
public var itemOverlayType = YPConfig.library.itemOverlayType
-
- override func awakeFromNib() {
- super.awakeFromNib()
-
+
+ init(frame: CGRect, zoomableView: YPAssetZoomableView) {
+ self.zoomableView = zoomableView
+ super.init(frame: frame)
+
+ self.zoomableView.zoomableViewDelegate = self
+
switch itemOverlayType {
case .grid:
itemOverlay = YPGridView()
default:
break
}
-
+
if let itemOverlay = itemOverlay {
addSubview(itemOverlay)
itemOverlay.frame = frame
clipsToBounds = true
-
+
itemOverlay.alpha = 0
}
-
- for sv in subviews {
- if let cv = sv as? YPAssetZoomableView {
- zoomableView = cv
- zoomableView?.myDelegate = self
- }
- }
-
+
let touchDownGR = UILongPressGestureRecognizer(target: self,
action: #selector(handleTouchDown))
touchDownGR.minimumPressDuration = 0
touchDownGR.delegate = self
addGestureRecognizer(touchDownGR)
-
+
// TODO: Add tap gesture to play/pause. Add double tap gesture to square/unsquare
-
+
subviews(
spinnerView.subviews(
spinner
),
curtain
)
-
+
spinner.centerInContainer()
spinnerView.fillContainer()
curtain.fillContainer()
-
+
spinner.startAnimating()
spinnerView.backgroundColor = UIColor.ypLabel.withAlphaComponent(0.3)
curtain.backgroundColor = UIColor.ypLabel.withAlphaComponent(0.7)
curtain.alpha = 0
-
+
if !onlySquare {
// Crop Button
squareCropButton.setImage(YPConfig.icons.cropIcon, for: .normal)
subviews(squareCropButton)
squareCropButton.size(42)
|-15-squareCropButton
- squareCropButton.Bottom == zoomableView!.Bottom - 15
+ squareCropButton.Bottom == self.Bottom - 15
}
-
+
// Multiple selection button
subviews(multipleSelectionButton)
- multipleSelectionButton.size(42)
- multipleSelectionButton-15-|
- multipleSelectionButton.setImage(YPConfig.icons.multipleSelectionOffIcon, for: .normal)
- multipleSelectionButton.Bottom == zoomableView!.Bottom - 15
-
+ multipleSelectionButton.size(42).trailing(15)
+ multipleSelectionButton.Bottom == self.Bottom - 15
}
-
+
+ required init?(coder: NSCoder) {
+ zoomableView = YPAssetZoomableView()
+ super.init(coder: coder)
+ fatalError("Only code layout.")
+ }
+
// MARK: - Square button
@objc public func squareCropButtonTapped() {
- if let zoomableView = zoomableView {
+// if let zoomableView = zoomableView {
let z = zoomableView.zoomScale
shouldCropToSquare = (z >= 1 && z < zoomableView.squaredZoomScale)
- }
- zoomableView?.fitImage(shouldCropToSquare, animated: true)
+// }
+ zoomableView.fitImage(shouldCropToSquare, animated: true)
}
public func refreshSquareCropButton() {
if onlySquare {
squareCropButton.isHidden = true
} else {
- if let image = zoomableView?.assetImageView.image {
+ if let image = zoomableView.assetImageView.image {
let isImageASquare = image.size.width == image.size.height
squareCropButton.isHidden = isImageASquare
}
}
let shouldFit = YPConfig.library.onlySquare ? true : shouldCropToSquare
- zoomableView?.fitImage(shouldFit)
- zoomableView?.layoutSubviews()
+ zoomableView.fitImage(shouldFit)
+ zoomableView.layoutSubviews()
}
// MARK: - Multiple selection
diff --git a/Source/Pages/Gallery/YPAssetZoomableView.swift b/Source/Pages/Gallery/YPAssetZoomableView.swift
index 1e492d4c1..bc0717c4b 100644
--- a/Source/Pages/Gallery/YPAssetZoomableView.swift
+++ b/Source/Pages/Gallery/YPAssetZoomableView.swift
@@ -17,7 +17,7 @@ protocol YPAssetZoomableViewDelegate: AnyObject {
}
final class YPAssetZoomableView: UIScrollView {
- public weak var myDelegate: YPAssetZoomableViewDelegate?
+ public weak var zoomableViewDelegate: YPAssetZoomableViewDelegate?
public var cropAreaDidChange = {}
public var isVideoMode = false
public var photoImageView = UIImageView()
@@ -92,7 +92,7 @@ final class YPAssetZoomableView: UIScrollView {
strongSelf.videoView.loadVideo(playerItem)
strongSelf.videoView.play()
- strongSelf.myDelegate?.ypAssetZoomableViewDidLayoutSubviews(strongSelf)
+ strongSelf.zoomableViewDelegate?.ypAssetZoomableViewDidLayoutSubviews(strongSelf)
}
}
@@ -143,10 +143,10 @@ final class YPAssetZoomableView: UIScrollView {
photoImageView.removeFromSuperview()
}
- required init?(coder aDecoder: NSCoder) {
- super.init(coder: aDecoder)!
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+
backgroundColor = YPConfig.colors.assetViewBackgroundColor
- frame.size = CGSize.zero
clipsToBounds = true
photoImageView.frame = CGRect(origin: CGPoint.zero, size: CGSize.zero)
videoView.frame = CGRect(origin: CGPoint.zero, size: CGSize.zero)
@@ -160,9 +160,14 @@ final class YPAssetZoomableView: UIScrollView {
isScrollEnabled = true
}
+ required init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)!
+ fatalError("Only code layout.")
+ }
+
override func layoutSubviews() {
super.layoutSubviews()
- myDelegate?.ypAssetZoomableViewDidLayoutSubviews(self)
+ zoomableViewDelegate?.ypAssetZoomableViewDidLayoutSubviews(self)
}
}
@@ -253,7 +258,7 @@ extension YPAssetZoomableView: UIScrollViewDelegate {
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
- myDelegate?.ypAssetZoomableViewScrollViewDidZoom()
+ zoomableViewDelegate?.ypAssetZoomableViewScrollViewDidZoom()
centerAssetView()
}
@@ -266,7 +271,7 @@ extension YPAssetZoomableView: UIScrollViewDelegate {
self.fitImage(true, animated: true)
}
- myDelegate?.ypAssetZoomableViewScrollViewDidEndZooming()
+ zoomableViewDelegate?.ypAssetZoomableViewScrollViewDidEndZooming()
cropAreaDidChange()
}
diff --git a/Source/Pages/Gallery/YPLibrary+LibraryChange.swift b/Source/Pages/Gallery/YPLibrary+LibraryChange.swift
index 34672f460..8b0991ca4 100644
--- a/Source/Pages/Gallery/YPLibrary+LibraryChange.swift
+++ b/Source/Pages/Gallery/YPLibrary+LibraryChange.swift
@@ -16,13 +16,13 @@ extension YPLibraryVC: PHPhotoLibraryChangeObserver {
public func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let fetchResult = self.mediaManager.fetchResult,
- let collectionChanges = changeInstance.changeDetails(for: fetchResult),
- let collectionView = self.v.collectionView else {
+ let collectionChanges = changeInstance.changeDetails(for: fetchResult) else {
ypLog("Some problems there.")
return
}
DispatchQueue.main.async {
+ let collectionView = self.v.collectionView
self.mediaManager.fetchResult = collectionChanges.fetchResultAfterChanges
if !collectionChanges.hasIncrementalChanges || collectionChanges.hasMoves {
collectionView.reloadData()
diff --git a/Source/Pages/Gallery/YPLibraryVC+CollectionView.swift b/Source/Pages/Gallery/YPLibraryVC+CollectionView.swift
index 60d43ae30..1b17cd385 100644
--- a/Source/Pages/Gallery/YPLibraryVC+CollectionView.swift
+++ b/Source/Pages/Gallery/YPLibraryVC+CollectionView.swift
@@ -12,7 +12,6 @@ extension YPLibraryVC {
var isLimitExceeded: Bool { return selectedItems.count >= YPConfig.library.maxNumberOfItems }
func setupCollectionView() {
- v.collectionView.backgroundColor = YPConfig.colors.libraryScreenBackgroundColor
v.collectionView.dataSource = self
v.collectionView.delegate = self
v.collectionView.register(YPLibraryViewCell.self, forCellWithReuseIdentifier: "YPLibraryViewCell")
diff --git a/Source/Pages/Gallery/YPLibraryVC+PanGesture.swift b/Source/Pages/Gallery/YPLibraryVC+PanGesture.swift
index 0e478f64a..c04890871 100644
--- a/Source/Pages/Gallery/YPLibraryVC+PanGesture.swift
+++ b/Source/Pages/Gallery/YPLibraryVC+PanGesture.swift
@@ -21,10 +21,12 @@ public class PanGestureHelper: NSObject, UIGestureRecognizerDelegate {
// The height constraint of the view with main selected image
var topHeight: CGFloat {
- get { return v.assetViewContainerConstraintTop.constant }
+ get {
+ return v.assetViewContainerConstraintTop?.constant ?? 0
+ }
set {
if newValue >= v.assetZoomableViewMinimalVisibleHeight - v.assetViewContainer.frame.height {
- v.assetViewContainerConstraintTop.constant = newValue
+ v.assetViewContainerConstraintTop?.constant = newValue
}
}
}
diff --git a/Source/Pages/Gallery/YPLibraryVC.swift b/Source/Pages/Gallery/YPLibraryVC.swift
index ff617c54c..9e36ba03f 100644
--- a/Source/Pages/Gallery/YPLibraryVC.swift
+++ b/Source/Pages/Gallery/YPLibraryVC.swift
@@ -10,9 +10,9 @@ import UIKit
import Photos
import PhotosUI
-internal class YPLibraryVC: UIViewController, YPPermissionCheckable {
+internal final class YPLibraryVC: UIViewController, YPPermissionCheckable {
internal weak var delegate: YPLibraryViewDelegate?
- internal var v: YPLibraryView!
+ internal var v = YPLibraryView(frame: .zero)
internal var isProcessing = false // true if video or image is in processing state
internal var selectedItems = [YPLibrarySelection]()
internal let mediaManager = LibraryMediaManager()
@@ -22,30 +22,20 @@ internal class YPLibraryVC: UIViewController, YPPermissionCheckable {
internal var isInitialized = false
// MARK: - Init
-
- internal required init(items: [YPMediaItem]?) {
+
+ internal override func loadView() {
+ view = v
+ }
+
+ required init() {
super.init(nibName: nil, bundle: nil)
title = YPConfig.wordings.libraryTitle
}
-
- internal convenience init() {
- self.init(items: nil)
- }
-
+
internal required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
- func setAlbum(_ album: YPAlbum) {
- title = album.title
- mediaManager.collection = album.collection
- currentlySelectedIndex = 0
- if !multipleSelectionEnabled {
- selectedItems.removeAll()
- }
- refreshMediaRequest()
- }
-
func initialize() {
guard isInitialized == false else {
return
@@ -54,7 +44,7 @@ internal class YPLibraryVC: UIViewController, YPPermissionCheckable {
defer {
isInitialized = true
}
-
+
mediaManager.initialize()
mediaManager.v = v
@@ -97,13 +87,18 @@ internal class YPLibraryVC: UIViewController, YPPermissionCheckable {
showMultipleSelection()
}
}
-
- // MARK: - View Lifecycle
-
- public override func loadView() {
- v = YPLibraryView.xibView()
- view = v
+
+ func setAlbum(_ album: YPAlbum) {
+ title = album.title
+ mediaManager.collection = album.collection
+ currentlySelectedIndex = 0
+ if !multipleSelectionEnabled {
+ selectedItems.removeAll()
+ }
+ refreshMediaRequest()
}
+
+ // MARK: - View Lifecycle
public override func viewDidLoad() {
super.viewDidLoad()
@@ -199,8 +194,8 @@ internal class YPLibraryVC: UIViewController, YPPermissionCheckable {
selectedItems = [
YPLibrarySelection(index: currentlySelectedIndex,
cropRect: v.currentCropRect(),
- scrollViewContentOffset: v.assetZoomableView!.contentOffset,
- scrollViewZoomScale: v.assetZoomableView!.zoomScale,
+ scrollViewContentOffset: v.assetZoomableView.contentOffset,
+ scrollViewZoomScale: v.assetZoomableView.zoomScale,
assetIdentifier: asset.localIdentifier)
]
}
diff --git a/Source/Pages/Gallery/YPLibraryView.swift b/Source/Pages/Gallery/YPLibraryView.swift
index a97b4d3be..d42db8866 100644
--- a/Source/Pages/Gallery/YPLibraryView.swift
+++ b/Source/Pages/Gallery/YPLibraryView.swift
@@ -10,20 +10,68 @@ import UIKit
import Stevia
import Photos
-final class YPLibraryView: UIView {
-
- let assetZoomableViewMinimalVisibleHeight: CGFloat = 50
-
- @IBOutlet weak var collectionView: UICollectionView!
- @IBOutlet weak var assetZoomableView: YPAssetZoomableView!
- @IBOutlet weak var assetViewContainer: YPAssetViewContainer!
- @IBOutlet weak var assetViewContainerConstraintTop: NSLayoutConstraint!
-
- let maxNumberWarningView = UIView()
- let maxNumberWarningLabel = UILabel()
- let progressView = UIProgressView()
- let line = UIView()
- var shouldShowLoader = false {
+internal final class YPLibraryView: UIView {
+
+ // MARK: - Public vars
+
+ internal let assetZoomableViewMinimalVisibleHeight: CGFloat = 50
+ internal var assetViewContainerConstraintTop: NSLayoutConstraint?
+ internal let collectionView: UICollectionView = {
+ let layout = UICollectionViewFlowLayout()
+ layout.scrollDirection = .vertical
+ let v = UICollectionView(frame: .zero, collectionViewLayout: layout)
+ v.backgroundColor = YPConfig.colors.libraryScreenBackgroundColor
+ v.collectionViewLayout = layout
+ v.showsHorizontalScrollIndicator = false
+ v.alwaysBounceVertical = true
+ return v
+ }()
+ internal lazy var assetViewContainer: YPAssetViewContainer = {
+ let v = YPAssetViewContainer(frame: .zero, zoomableView: assetZoomableView)
+ v.accessibilityIdentifier = "assetViewContainer"
+ return v
+ }()
+ internal let assetZoomableView: YPAssetZoomableView = {
+ let v = YPAssetZoomableView(frame: .zero)
+ v.accessibilityIdentifier = "assetZoomableView"
+ return v
+ }()
+ /// At the bottom there is a view that is visible when selected a limit of items with multiple selection
+ internal let maxNumberWarningView: UIView = {
+ let v = UIView()
+ v.backgroundColor = .ypSecondarySystemBackground
+ v.isHidden = true
+ return v
+ }()
+ internal let maxNumberWarningLabel: UILabel = {
+ let v = UILabel()
+ v.font = YPConfig.fonts.libaryWarningFont
+ return v
+ }()
+
+ // MARK: - Private vars
+
+ private let line: UIView = {
+ let v = UIView()
+ v.backgroundColor = .ypSystemBackground
+ return v
+ }()
+ /// When video is processing this bar appears
+ private let progressView: UIProgressView = {
+ let v = UIProgressView()
+ v.progressViewStyle = .bar
+ v.trackTintColor = YPConfig.colors.progressBarTrackColor
+ v.progressTintColor = YPConfig.colors.progressBarCompletedColor ?? YPConfig.colors.tintColor
+ v.isHidden = true
+ v.isUserInteractionEnabled = false
+ return v
+ }()
+ private let collectionContainerView: UIView = {
+ let v = UIView()
+ v.accessibilityIdentifier = "collectionContainerView"
+ return v
+ }()
+ private var shouldShowLoader = false {
didSet {
DispatchQueue.main.async {
self.assetViewContainer.squareCropButton.isEnabled = !self.shouldShowLoader
@@ -33,85 +81,31 @@ final class YPLibraryView: UIView {
}
}
}
-
- override func awakeFromNib() {
- super.awakeFromNib()
-
- subviews(
- line
- )
-
- layout(
- assetViewContainer!,
- |line| ~ 1
- )
-
- line.backgroundColor = .ypSystemBackground
-
- setupMaxNumberOfItemsView()
- setupProgressBarView()
- }
-
- /// At the bottom there is a view that is visible when selected a limit of items with multiple selection
- func setupMaxNumberOfItemsView() {
- // View Hierarchy
- subviews(
- maxNumberWarningView.subviews(
- maxNumberWarningLabel
- )
- )
-
- // Layout
- |maxNumberWarningView|.bottom(0)
- if #available(iOS 11.0, *) {
- maxNumberWarningView.Top == safeAreaLayoutGuide.Bottom - 40
- maxNumberWarningLabel.centerHorizontally().top(11)
- } else {
- maxNumberWarningView.height(40)
- maxNumberWarningLabel.centerInContainer()
- }
-
- // Style
- maxNumberWarningView.backgroundColor = .ypSecondarySystemBackground
- maxNumberWarningLabel.font = YPConfig.fonts.libaryWarningFont
- maxNumberWarningView.isHidden = true
+
+ // MARK: - Init
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+
+ setupLayout()
+ clipsToBounds = true
}
-
- /// When video is processing this bar appears
- func setupProgressBarView() {
- subviews(
- progressView
- )
-
- progressView.height(5)
- progressView.Top == line.Top
- progressView.Width == line.Width
- progressView.progressViewStyle = .bar
- progressView.trackTintColor = YPConfig.colors.progressBarTrackColor
- progressView.progressTintColor = YPConfig.colors.progressBarCompletedColor ?? YPConfig.colors.tintColor
- progressView.isHidden = true
- progressView.isUserInteractionEnabled = false
+
+ required init?(coder: NSCoder) {
+ super.init(coder: coder)
+ fatalError("Only code layout.")
}
-}
-// MARK: - UI Helpers
+ // MARK: - Public Methods
+
+ // MARK: Overlay view
-extension YPLibraryView {
-
- class func xibView() -> YPLibraryView? {
- let nib = UINib(nibName: "YPLibraryView", bundle: Bundle.local)
- let xibView = nib.instantiate(withOwner: self, options: nil)[0] as? YPLibraryView
- return xibView
- }
-
- // MARK: - Overlay view
-
func hideOverlayView() {
assetViewContainer.itemOverlay?.alpha = 0
}
-
- // MARK: - Loader and progress
-
+
+ // MARK: Loader and progress
+
func fadeInLoader() {
shouldShowLoader = true
// Only show loader if full res image takes more than 0.5s to load.
@@ -130,39 +124,37 @@ extension YPLibraryView {
}
}
}
-
+
func hideLoader() {
shouldShowLoader = false
assetViewContainer.spinnerView.alpha = 0
}
-
+
func updateProgress(_ progress: Float) {
progressView.isHidden = progress > 0.99 || progress == 0
progressView.progress = progress
UIView.animate(withDuration: 0.1, animations: progressView.layoutIfNeeded)
}
-
- // MARK: - Crop Rect
-
+
+ // MARK: Crop Rect
+
func currentCropRect() -> CGRect {
- guard let cropView = assetZoomableView else {
- return CGRect.zero
- }
+ let cropView = assetZoomableView
let normalizedX = min(1, cropView.contentOffset.x &/ cropView.contentSize.width)
let normalizedY = min(1, cropView.contentOffset.y &/ cropView.contentSize.height)
let normalizedWidth = min(1, cropView.frame.width / cropView.contentSize.width)
let normalizedHeight = min(1, cropView.frame.height / cropView.contentSize.height)
return CGRect(x: normalizedX, y: normalizedY, width: normalizedWidth, height: normalizedHeight)
}
-
- // MARK: - Curtain
-
+
+ // MARK: Curtain
+
func refreshImageCurtainAlpha() {
- let imageCurtainAlpha = abs(assetViewContainerConstraintTop.constant)
- / (assetViewContainer.frame.height - assetZoomableViewMinimalVisibleHeight)
+ let imageCurtainAlpha = abs(assetViewContainerConstraintTop?.constant ?? 0)
+ / (assetViewContainer.frame.height - assetZoomableViewMinimalVisibleHeight)
assetViewContainer.curtain.alpha = imageCurtainAlpha
}
-
+
func cellSize() -> CGSize {
var screenWidth: CGFloat = UIScreen.main.bounds.width
if UIDevice.current.userInterfaceIdiom == .pad && YPImagePickerConfiguration.widthOniPad > 0 {
@@ -171,4 +163,42 @@ extension YPLibraryView {
let size = screenWidth / 4 * UIScreen.main.scale
return CGSize(width: size, height: size)
}
+
+ // MARK: - Private Methods
+
+ private func setupLayout() {
+ subviews(
+ collectionContainerView.subviews(
+ collectionView
+ ),
+ line,
+ assetViewContainer.subviews(
+ assetZoomableView
+ ),
+ progressView,
+ maxNumberWarningView.subviews(
+ maxNumberWarningLabel
+ )
+ )
+
+ collectionContainerView.fillContainer()
+ collectionView.fillHorizontally().bottom(0)
+
+ assetViewContainer.Bottom == line.Top
+ line.height(1)
+ line.fillHorizontally()
+
+ assetViewContainer.top(0).fillHorizontally().heightEqualsWidth()
+ self.assetViewContainerConstraintTop = assetViewContainer.topConstraint
+ assetZoomableView.fillContainer().heightEqualsWidth()
+ assetZoomableView.Bottom == collectionView.Top
+ assetViewContainer.sendSubviewToBack(assetZoomableView)
+
+ progressView.height(5).fillHorizontally()
+ progressView.Bottom == line.Top
+
+ |maxNumberWarningView|.bottom(0)
+ maxNumberWarningView.Top == safeAreaLayoutGuide.Bottom - 40
+ maxNumberWarningLabel.centerHorizontally().top(11)
+ }
}
diff --git a/Source/Pages/Gallery/YPLibraryView.xib b/Source/Pages/Gallery/YPLibraryView.xib
index 6048a783b..58887657d 100644
--- a/Source/Pages/Gallery/YPLibraryView.xib
+++ b/Source/Pages/Gallery/YPLibraryView.xib
@@ -4,6 +4,7 @@
+
@@ -39,12 +40,13 @@
+
-
+
@@ -76,4 +78,9 @@
+
+
+
+
+