1173 lines
46 KiB
Swift
1173 lines
46 KiB
Swift
//
|
||
// ZLPhotoPreviewController.swift
|
||
// ZLPhotoBrowser
|
||
//
|
||
// Created by long on 2020/8/20.
|
||
//
|
||
// Copyright (c) 2020 Long Zhang <495181165@qq.com>
|
||
//
|
||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
// of this software and associated documentation files (the "Software"), to deal
|
||
// in the Software without restriction, including without limitation the rights
|
||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
// copies of the Software, and to permit persons to whom the Software is
|
||
// furnished to do so, subject to the following conditions:
|
||
//
|
||
// The above copyright notice and this permission notice shall be included in
|
||
// all copies or substantial portions of the Software.
|
||
//
|
||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
// THE SOFTWARE.
|
||
|
||
import UIKit
|
||
import Photos
|
||
|
||
class ZLPhotoPreviewController: UIViewController {
|
||
static let colItemSpacing: CGFloat = 40
|
||
|
||
static let selPhotoPreviewH: CGFloat = 100
|
||
|
||
static let previewVCScrollNotification = Notification.Name("previewVCScrollNotification")
|
||
|
||
let arrDataSources: [ZLPhotoModel]
|
||
|
||
var currentIndex: Int
|
||
|
||
lazy var collectionView: UICollectionView = {
|
||
let layout = ZLCollectionViewFlowLayout()
|
||
layout.scrollDirection = .horizontal
|
||
|
||
let view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||
view.backgroundColor = .clear
|
||
view.dataSource = self
|
||
view.delegate = self
|
||
view.isPagingEnabled = true
|
||
view.showsHorizontalScrollIndicator = false
|
||
|
||
ZLPhotoPreviewCell.zl.register(view)
|
||
ZLGifPreviewCell.zl.register(view)
|
||
ZLLivePhotoPreviewCell.zl.register(view)
|
||
ZLVideoPreviewCell.zl.register(view)
|
||
|
||
return view
|
||
}()
|
||
|
||
private let showBottomViewAndSelectBtn: Bool
|
||
|
||
private var indexBeforOrientationChanged: Int
|
||
|
||
private lazy var navView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .zl.navBarColorOfPreviewVC
|
||
return view
|
||
}()
|
||
|
||
private var navBlurView: UIVisualEffectView?
|
||
|
||
private lazy var backBtn: UIButton = {
|
||
let btn = UIButton(type: .custom)
|
||
var image = UIImage.zl.getImage("zl_navBack")
|
||
if isRTL() {
|
||
image = image?.imageFlippedForRightToLeftLayoutDirection()
|
||
btn.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -10)
|
||
} else {
|
||
btn.imageEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 0)
|
||
}
|
||
btn.setImage(image, for: .normal)
|
||
btn.addTarget(self, action: #selector(backBtnClick), for: .touchUpInside)
|
||
return btn
|
||
}()
|
||
|
||
private lazy var selectBtn: ZLEnlargeButton = {
|
||
let btn = ZLEnlargeButton(type: .custom)
|
||
btn.setImage(.zl.getImage("zl_btn_circle"), for: .normal)
|
||
btn.setImage(.zl.getImage("zl_btn_selected"), for: .selected)
|
||
btn.enlargeInset = 10
|
||
btn.addTarget(self, action: #selector(selectBtnClick), for: .touchUpInside)
|
||
return btn
|
||
}()
|
||
|
||
private lazy var indexLabel: UILabel = {
|
||
let label = UILabel()
|
||
label.backgroundColor = .zl.indexLabelBgColor
|
||
label.font = .zl.font(ofSize: 14)
|
||
label.textColor = .white
|
||
label.textAlignment = .center
|
||
label.layer.cornerRadius = 25.0 / 2
|
||
label.layer.masksToBounds = true
|
||
label.isHidden = true
|
||
return label
|
||
}()
|
||
|
||
private lazy var bottomView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .zl.bottomToolViewBgColorOfPreviewVC
|
||
return view
|
||
}()
|
||
|
||
private var bottomBlurView: UIVisualEffectView?
|
||
|
||
private lazy var editBtn: UIButton = {
|
||
let btn = createBtn(localLanguageTextValue(.edit), #selector(editBtnClick))
|
||
btn.titleLabel?.lineBreakMode = .byCharWrapping
|
||
btn.titleLabel?.numberOfLines = 0
|
||
btn.contentHorizontalAlignment = .left
|
||
return btn
|
||
}()
|
||
|
||
private lazy var originalBtn: UIButton = {
|
||
let btn = createBtn(localLanguageTextValue(.originalPhoto), #selector(originalPhotoClick))
|
||
btn.titleLabel?.lineBreakMode = .byCharWrapping
|
||
btn.titleLabel?.numberOfLines = 2
|
||
btn.contentHorizontalAlignment = .left
|
||
btn.setImage(.zl.getImage("zl_btn_original_circle"), for: .normal)
|
||
btn.setImage(.zl.getImage("zl_btn_original_selected"), for: .selected)
|
||
btn.setImage(.zl.getImage("zl_btn_original_selected"), for: [.selected, .highlighted])
|
||
btn.adjustsImageWhenHighlighted = false
|
||
if isRTL() {
|
||
btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 5)
|
||
} else {
|
||
btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 0)
|
||
}
|
||
return btn
|
||
}()
|
||
|
||
private lazy var doneBtn: UIButton = {
|
||
let btn = createBtn(localLanguageTextValue(.done), #selector(doneBtnClick), true)
|
||
btn.backgroundColor = .zl.bottomToolViewBtnNormalBgColorOfPreviewVC
|
||
btn.layer.masksToBounds = true
|
||
btn.layer.cornerRadius = ZLLayout.bottomToolBtnCornerRadius
|
||
return btn
|
||
}()
|
||
|
||
private var selPhotoPreview: ZLPhotoPreviewSelectedView?
|
||
|
||
private var isFirstAppear = true
|
||
|
||
private var hideNavView = false
|
||
|
||
private var popInteractiveTransition: ZLPhotoPreviewPopInteractiveTransition?
|
||
|
||
private var orientation: UIInterfaceOrientation = .unknown
|
||
|
||
/// 是否在点击确定时候,当未选择任何照片时候,自动选择当前index的照片
|
||
var autoSelectCurrentIfNotSelectAnyone = true
|
||
|
||
/// 界面消失时,通知上个界面刷新(针对预览视图)
|
||
var backBlock: (() -> Void)?
|
||
|
||
override var prefersStatusBarHidden: Bool {
|
||
return !ZLPhotoUIConfiguration.default().showStatusBarInPreviewInterface
|
||
}
|
||
|
||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||
return ZLPhotoUIConfiguration.default().statusBarStyle
|
||
}
|
||
|
||
deinit {
|
||
zl_debugPrint("ZLPhotoPreviewController deinit")
|
||
}
|
||
|
||
init(photos: [ZLPhotoModel], index: Int, showBottomViewAndSelectBtn: Bool = true) {
|
||
arrDataSources = photos
|
||
self.showBottomViewAndSelectBtn = showBottomViewAndSelectBtn
|
||
currentIndex = min(index, photos.count - 1)
|
||
indexBeforOrientationChanged = currentIndex
|
||
super.init(nibName: nil, bundle: nil)
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder _: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
|
||
setupUI()
|
||
|
||
addPopInteractiveTransition()
|
||
resetSubViewStatus()
|
||
}
|
||
|
||
override func viewWillAppear(_ animated: Bool) {
|
||
super.viewWillAppear(animated)
|
||
navigationController?.navigationBar.isHidden = true
|
||
}
|
||
|
||
override func viewDidAppear(_ animated: Bool) {
|
||
super.viewDidAppear(animated)
|
||
navigationController?.delegate = self
|
||
|
||
guard isFirstAppear else { return }
|
||
isFirstAppear = false
|
||
|
||
reloadCurrentCell()
|
||
}
|
||
|
||
override func viewDidLayoutSubviews() {
|
||
super.viewDidLayoutSubviews()
|
||
|
||
var insets = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
|
||
if #available(iOS 11.0, *) {
|
||
insets = self.view.safeAreaInsets
|
||
}
|
||
insets.top = max(20, insets.top)
|
||
|
||
collectionView.frame = CGRect(
|
||
x: -ZLPhotoPreviewController.colItemSpacing / 2,
|
||
y: 0,
|
||
width: view.zl.width + ZLPhotoPreviewController.colItemSpacing,
|
||
height: view.zl.height
|
||
)
|
||
|
||
let navH = insets.top + 44
|
||
navView.frame = CGRect(x: 0, y: 0, width: view.zl.width, height: navH)
|
||
navBlurView?.frame = navView.bounds
|
||
|
||
if isRTL() {
|
||
backBtn.frame = CGRect(x: view.zl.width - insets.right - 60, y: insets.top, width: 60, height: 44)
|
||
selectBtn.frame = CGRect(x: insets.left + 15, y: insets.top + (44 - 25) / 2, width: 25, height: 25)
|
||
} else {
|
||
backBtn.frame = CGRect(x: insets.left, y: insets.top, width: 60, height: 44)
|
||
selectBtn.frame = CGRect(x: view.zl.width - 40 - insets.right, y: insets.top + (44 - 25) / 2, width: 25, height: 25)
|
||
}
|
||
|
||
indexLabel.frame = selectBtn.bounds
|
||
|
||
refreshBottomViewFrame()
|
||
|
||
let ori = UIApplication.shared.statusBarOrientation
|
||
if ori != orientation {
|
||
orientation = ori
|
||
|
||
collectionView.performBatchUpdates(nil) { _ in
|
||
self.collectionView.setContentOffset(
|
||
CGPoint(
|
||
x: (self.view.zl.width + ZLPhotoPreviewController.colItemSpacing) * CGFloat(self.indexBeforOrientationChanged),
|
||
y: 0
|
||
),
|
||
animated: false
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
private func reloadCurrentCell() {
|
||
guard let cell = collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0)) else {
|
||
return
|
||
}
|
||
|
||
if let cell = cell as? ZLGifPreviewCell {
|
||
cell.loadGifWhenCellDisplaying()
|
||
} else if let cell = cell as? ZLLivePhotoPreviewCell {
|
||
cell.loadLivePhotoData()
|
||
}
|
||
}
|
||
|
||
private func refreshBottomViewFrame() {
|
||
var insets = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
|
||
if #available(iOS 11.0, *) {
|
||
insets = view.safeAreaInsets
|
||
}
|
||
var bottomViewH = ZLLayout.bottomToolViewH
|
||
|
||
var showSelPhotoPreview = false
|
||
if ZLPhotoConfiguration.default().showSelectedPhotoPreview,
|
||
let nav = navigationController as? ZLImageNavController,
|
||
!nav.arrSelectedModels.isEmpty {
|
||
showSelPhotoPreview = true
|
||
bottomViewH += ZLPhotoPreviewController.selPhotoPreviewH
|
||
selPhotoPreview?.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: ZLPhotoPreviewController.selPhotoPreviewH)
|
||
}
|
||
|
||
let btnH = ZLLayout.bottomToolBtnH
|
||
|
||
bottomView.frame = CGRect(x: 0, y: view.frame.height - insets.bottom - bottomViewH, width: view.frame.width, height: bottomViewH + insets.bottom)
|
||
bottomBlurView?.frame = bottomView.bounds
|
||
|
||
let btnY: CGFloat = showSelPhotoPreview ? ZLPhotoPreviewController.selPhotoPreviewH + ZLLayout.bottomToolBtnY : ZLLayout.bottomToolBtnY
|
||
|
||
let btnMaxWidth = (bottomView.bounds.width - 30) / 3
|
||
|
||
let editTitle = localLanguageTextValue(.edit)
|
||
let editBtnW = editTitle.zl.boundingRect(font: ZLLayout.bottomToolTitleFont, limitSize: CGSize(width: CGFloat.greatestFiniteMagnitude, height: 30)).width
|
||
editBtn.frame = CGRect(x: 15, y: btnY, width: min(btnMaxWidth, editBtnW), height: btnH)
|
||
|
||
let originalTitle = localLanguageTextValue(.originalPhoto)
|
||
let originBtnW = originalTitle.zl.boundingRect(
|
||
font: ZLLayout.bottomToolTitleFont,
|
||
limitSize: CGSize(
|
||
width: CGFloat.greatestFiniteMagnitude,
|
||
height: 30
|
||
)
|
||
).width + (originalBtn.currentImage?.size.width ?? 19) + 12
|
||
let originBtnMaxW = min(btnMaxWidth, originBtnW)
|
||
originalBtn.frame = CGRect(x: (bottomView.bounds.width - originBtnMaxW) / 2 - 5, y: btnY, width: originBtnMaxW, height: btnH)
|
||
|
||
let selCount = (navigationController as? ZLImageNavController)?.arrSelectedModels.count ?? 0
|
||
var doneTitle = localLanguageTextValue(.done)
|
||
if ZLPhotoConfiguration.default().showSelectCountOnDoneBtn, selCount > 0 {
|
||
doneTitle += "(" + String(selCount) + ")"
|
||
}
|
||
let doneBtnW = doneTitle.zl.boundingRect(font: ZLLayout.bottomToolTitleFont, limitSize: CGSize(width: CGFloat.greatestFiniteMagnitude, height: 30)).width + 20
|
||
doneBtn.frame = CGRect(x: bottomView.bounds.width - doneBtnW - 15, y: btnY, width: doneBtnW, height: btnH)
|
||
}
|
||
|
||
private func setupUI() {
|
||
view.backgroundColor = .zl.previewVCBgColor
|
||
automaticallyAdjustsScrollViewInsets = false
|
||
|
||
let config = ZLPhotoConfiguration.default()
|
||
|
||
view.addSubview(navView)
|
||
|
||
if let effect = ZLPhotoUIConfiguration.default().navViewBlurEffectOfPreview {
|
||
navBlurView = UIVisualEffectView(effect: effect)
|
||
navView.addSubview(navBlurView!)
|
||
}
|
||
|
||
navView.addSubview(backBtn)
|
||
navView.addSubview(selectBtn)
|
||
selectBtn.addSubview(indexLabel)
|
||
view.addSubview(collectionView)
|
||
view.addSubview(bottomView)
|
||
|
||
if let effect = ZLPhotoUIConfiguration.default().bottomViewBlurEffectOfPreview {
|
||
bottomBlurView = UIVisualEffectView(effect: effect)
|
||
bottomView.addSubview(bottomBlurView!)
|
||
}
|
||
|
||
if config.showSelectedPhotoPreview {
|
||
let selModels = (navigationController as? ZLImageNavController)?.arrSelectedModels ?? []
|
||
selPhotoPreview = ZLPhotoPreviewSelectedView(selModels: selModels, currentShowModel: arrDataSources[currentIndex])
|
||
selPhotoPreview?.selectBlock = { [weak self] model in
|
||
self?.scrollToSelPreviewCell(model)
|
||
}
|
||
selPhotoPreview?.endSortBlock = { [weak self] models in
|
||
self?.refreshCurrentCellIndex(models)
|
||
}
|
||
bottomView.addSubview(selPhotoPreview!)
|
||
}
|
||
|
||
editBtn.isHidden = (!config.allowEditImage && !config.allowEditVideo)
|
||
bottomView.addSubview(editBtn)
|
||
|
||
originalBtn.isHidden = !(config.allowSelectOriginal && config.allowSelectImage)
|
||
originalBtn.isSelected = (navigationController as? ZLImageNavController)?.isSelectedOriginal ?? false
|
||
bottomView.addSubview(originalBtn)
|
||
|
||
bottomView.addSubview(doneBtn)
|
||
|
||
view.bringSubviewToFront(navView)
|
||
}
|
||
|
||
private func createBtn(_ title: String, _ action: Selector, _ isDone: Bool = false) -> UIButton {
|
||
let btn = UIButton(type: .custom)
|
||
btn.titleLabel?.font = ZLLayout.bottomToolTitleFont
|
||
btn.setTitle(title, for: .normal)
|
||
btn.setTitleColor(
|
||
isDone ? .zl.bottomToolViewDoneBtnNormalTitleColorOfPreviewVC : .zl.bottomToolViewBtnNormalTitleColorOfPreviewVC,
|
||
for: .normal
|
||
)
|
||
btn.setTitleColor(
|
||
isDone ? .zl.bottomToolViewDoneBtnDisableTitleColorOfPreviewVC : .zl.bottomToolViewBtnDisableTitleColorOfPreviewVC,
|
||
for: .disabled
|
||
)
|
||
btn.addTarget(self, action: action, for: .touchUpInside)
|
||
return btn
|
||
}
|
||
|
||
private func addPopInteractiveTransition() {
|
||
guard (navigationController?.viewControllers.count ?? 0) > 1 else {
|
||
// 仅有当前vc一个时候,说明不是从相册进入,不添加交互动画
|
||
return
|
||
}
|
||
popInteractiveTransition = ZLPhotoPreviewPopInteractiveTransition(viewController: self)
|
||
popInteractiveTransition?.shouldStartTransition = { [weak self] point -> Bool in
|
||
guard let `self` = self else { return false }
|
||
if !self.hideNavView, self.navView.frame.contains(point) || self.bottomView.frame.contains(point) {
|
||
return false
|
||
}
|
||
|
||
guard self.collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)) != nil else {
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
popInteractiveTransition?.startTransition = { [weak self] in
|
||
guard let `self` = self else { return }
|
||
|
||
self.navView.alpha = 0
|
||
self.bottomView.alpha = 0
|
||
|
||
guard let cell = self.collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)) else {
|
||
return
|
||
}
|
||
if cell is ZLVideoPreviewCell {
|
||
(cell as! ZLVideoPreviewCell).pauseWhileTransition()
|
||
} else if cell is ZLLivePhotoPreviewCell {
|
||
(cell as! ZLLivePhotoPreviewCell).livePhotoView.stopPlayback()
|
||
} else if cell is ZLGifPreviewCell {
|
||
(cell as! ZLGifPreviewCell).pauseGif()
|
||
}
|
||
}
|
||
popInteractiveTransition?.cancelTransition = { [weak self] in
|
||
guard let `self` = self else { return }
|
||
|
||
self.hideNavView = false
|
||
self.navView.isHidden = false
|
||
self.bottomView.isHidden = false
|
||
UIView.animate(withDuration: 0.5) {
|
||
self.navView.alpha = 1
|
||
self.bottomView.alpha = 1
|
||
}
|
||
|
||
guard let cell = self.collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)) else {
|
||
return
|
||
}
|
||
if cell is ZLGifPreviewCell {
|
||
(cell as! ZLGifPreviewCell).resumeGif()
|
||
}
|
||
}
|
||
}
|
||
|
||
private func resetSubViewStatus() {
|
||
guard let nav = navigationController as? ZLImageNavController else {
|
||
zlLoggerInDebug("Navigation controller is null")
|
||
return
|
||
}
|
||
let config = ZLPhotoConfiguration.default()
|
||
let currentModel = arrDataSources[currentIndex]
|
||
|
||
if (!config.allowMixSelect && currentModel.type == .video) || (!config.showSelectBtnWhenSingleSelect && config.maxSelectCount == 1) {
|
||
selectBtn.isHidden = true
|
||
} else {
|
||
selectBtn.isHidden = false
|
||
}
|
||
selectBtn.isSelected = arrDataSources[currentIndex].isSelected
|
||
resetIndexLabelStatus()
|
||
|
||
guard showBottomViewAndSelectBtn else {
|
||
selectBtn.isHidden = true
|
||
bottomView.isHidden = true
|
||
return
|
||
}
|
||
let selCount = nav.arrSelectedModels.count
|
||
var doneTitle = localLanguageTextValue(.done)
|
||
if ZLPhotoConfiguration.default().showSelectCountOnDoneBtn, selCount > 0 {
|
||
doneTitle += "(" + String(selCount) + ")"
|
||
}
|
||
doneBtn.setTitle(doneTitle, for: .normal)
|
||
|
||
selPhotoPreview?.isHidden = selCount == 0
|
||
refreshBottomViewFrame()
|
||
|
||
var hideEditBtn = true
|
||
if selCount < config.maxSelectCount || nav.arrSelectedModels.contains(where: { $0 == currentModel }) {
|
||
if config.allowEditImage,
|
||
currentModel.type == .image || (currentModel.type == .gif && !config.allowSelectGif) || (currentModel.type == .livePhoto && !config.allowSelectLivePhoto) {
|
||
hideEditBtn = false
|
||
}
|
||
if config.allowEditVideo,
|
||
currentModel.type == .video,
|
||
selCount == 0 || (selCount == 1 && nav.arrSelectedModels.first == currentModel) {
|
||
hideEditBtn = false
|
||
}
|
||
}
|
||
editBtn.isHidden = hideEditBtn
|
||
|
||
if ZLPhotoConfiguration.default().allowSelectOriginal,
|
||
ZLPhotoConfiguration.default().allowSelectImage {
|
||
originalBtn.isHidden = !((currentModel.type == .image) || (currentModel.type == .livePhoto && !config.allowSelectLivePhoto) || (currentModel.type == .gif && !config.allowSelectGif))
|
||
}
|
||
}
|
||
|
||
private func resetIndexLabelStatus() {
|
||
guard ZLPhotoConfiguration.default().showSelectedIndex else {
|
||
indexLabel.isHidden = true
|
||
return
|
||
}
|
||
guard let nav = navigationController as? ZLImageNavController else {
|
||
zlLoggerInDebug("Navigation controller is null")
|
||
return
|
||
}
|
||
if let index = nav.arrSelectedModels.firstIndex(where: { $0 == self.arrDataSources[self.currentIndex] }) {
|
||
indexLabel.isHidden = false
|
||
indexLabel.text = String(index + 1)
|
||
} else {
|
||
indexLabel.isHidden = true
|
||
}
|
||
}
|
||
|
||
// MARK: btn actions
|
||
|
||
@objc private func backBtnClick() {
|
||
backBlock?()
|
||
let vc = navigationController?.popViewController(animated: true)
|
||
if vc == nil {
|
||
navigationController?.dismiss(animated: true, completion: nil)
|
||
}
|
||
}
|
||
|
||
@objc private func selectBtnClick() {
|
||
guard let nav = navigationController as? ZLImageNavController else {
|
||
zlLoggerInDebug("Navigation controller is null")
|
||
return
|
||
}
|
||
|
||
let config = ZLPhotoConfiguration.default()
|
||
|
||
let currentModel = arrDataSources[currentIndex]
|
||
selectBtn.layer.removeAllAnimations()
|
||
if currentModel.isSelected {
|
||
currentModel.isSelected = false
|
||
nav.arrSelectedModels.removeAll { $0 == currentModel }
|
||
selPhotoPreview?.removeSelModel(model: currentModel)
|
||
|
||
config.didDeselectAsset?(currentModel.asset)
|
||
} else {
|
||
if config.animateSelectBtnWhenSelect {
|
||
selectBtn.layer.add(ZLAnimationUtils.springAnimation(), forKey: nil)
|
||
}
|
||
if !canAddModel(currentModel, currentSelectCount: nav.arrSelectedModels.count, sender: self) {
|
||
return
|
||
}
|
||
currentModel.isSelected = true
|
||
nav.arrSelectedModels.append(currentModel)
|
||
selPhotoPreview?.addSelModel(model: currentModel)
|
||
|
||
config.didSelectAsset?(currentModel.asset)
|
||
}
|
||
resetSubViewStatus()
|
||
}
|
||
|
||
@objc private func editBtnClick() {
|
||
let config = ZLPhotoConfiguration.default()
|
||
let model = arrDataSources[currentIndex]
|
||
|
||
var requestAvAssetID: PHImageRequestID?
|
||
let hud = ZLProgressHUD(style: ZLPhotoUIConfiguration.default().hudStyle)
|
||
hud.timeoutBlock = { [weak self] in
|
||
showAlertView(localLanguageTextValue(.timeout), self)
|
||
if let requestAvAssetID = requestAvAssetID {
|
||
PHImageManager.default().cancelImageRequest(requestAvAssetID)
|
||
}
|
||
}
|
||
|
||
if model.type == .image || (!config.allowSelectGif && model.type == .gif) || (!config.allowSelectLivePhoto && model.type == .livePhoto) {
|
||
hud.show(timeout: ZLPhotoConfiguration.default().timeout)
|
||
requestAvAssetID = ZLPhotoManager.fetchImage(for: model.asset, size: model.previewSize) { [weak self] image, isDegraded in
|
||
if !isDegraded {
|
||
if let image = image {
|
||
self?.showEditImageVC(image: image)
|
||
} else {
|
||
showAlertView(localLanguageTextValue(.imageLoadFailed), self)
|
||
}
|
||
hud.hide()
|
||
}
|
||
}
|
||
} else if model.type == .video || config.allowEditVideo {
|
||
hud.show(timeout: ZLPhotoConfiguration.default().timeout)
|
||
// fetch avasset
|
||
requestAvAssetID = ZLPhotoManager.fetchAVAsset(forVideo: model.asset) { [weak self] avAsset, _ in
|
||
hud.hide()
|
||
if let avAsset = avAsset {
|
||
self?.showEditVideoVC(model: model, avAsset: avAsset)
|
||
} else {
|
||
showAlertView(localLanguageTextValue(.timeout), self)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc private func originalPhotoClick() {
|
||
originalBtn.isSelected.toggle()
|
||
|
||
let config = ZLPhotoConfiguration.default()
|
||
|
||
let nav = (navigationController as? ZLImageNavController)
|
||
nav?.isSelectedOriginal = originalBtn.isSelected
|
||
if nav?.arrSelectedModels.isEmpty == true {
|
||
selectBtnClick()
|
||
} else if config.maxSelectCount == 1,
|
||
!config.showSelectBtnWhenSingleSelect,
|
||
!originalBtn.isSelected,
|
||
nav?.arrSelectedModels.count == 1,
|
||
let currentModel = nav?.arrSelectedModels.first {
|
||
currentModel.isSelected = false
|
||
currentModel.editImage = nil
|
||
currentModel.editImageModel = nil
|
||
nav?.arrSelectedModels.removeAll { $0 == currentModel }
|
||
selPhotoPreview?.removeSelModel(model: currentModel)
|
||
resetSubViewStatus()
|
||
let index = config.sortAscending ? arrDataSources.lastIndex { $0 == currentModel } : arrDataSources.firstIndex { $0 == currentModel }
|
||
if let index = index {
|
||
collectionView.reloadItems(at: [IndexPath(row: index, section: 0)])
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc private func doneBtnClick() {
|
||
guard let nav = navigationController as? ZLImageNavController else {
|
||
zlLoggerInDebug("Navigation controller is null")
|
||
return
|
||
}
|
||
|
||
func callBackBeforeDone() {
|
||
if let block = ZLPhotoConfiguration.default().operateBeforeDoneAction {
|
||
block(self) { [weak nav] in
|
||
nav?.selectImageBlock?()
|
||
}
|
||
} else {
|
||
nav.selectImageBlock?()
|
||
}
|
||
}
|
||
|
||
let currentModel = arrDataSources[currentIndex]
|
||
if autoSelectCurrentIfNotSelectAnyone {
|
||
if nav.arrSelectedModels.isEmpty, canAddModel(currentModel, currentSelectCount: nav.arrSelectedModels.count, sender: self) {
|
||
nav.arrSelectedModels.append(currentModel)
|
||
|
||
ZLPhotoConfiguration.default().didSelectAsset?(currentModel.asset)
|
||
}
|
||
|
||
if !nav.arrSelectedModels.isEmpty {
|
||
callBackBeforeDone()
|
||
}
|
||
} else {
|
||
callBackBeforeDone()
|
||
}
|
||
}
|
||
|
||
private func scrollToSelPreviewCell(_ model: ZLPhotoModel) {
|
||
guard let index = arrDataSources.lastIndex(of: model) else {
|
||
return
|
||
}
|
||
collectionView.performBatchUpdates({
|
||
self.collectionView.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: false)
|
||
}) { _ in
|
||
self.indexBeforOrientationChanged = self.currentIndex
|
||
self.reloadCurrentCell()
|
||
}
|
||
}
|
||
|
||
private func refreshCurrentCellIndex(_ models: [ZLPhotoModel]) {
|
||
let nav = navigationController as? ZLImageNavController
|
||
nav?.arrSelectedModels.removeAll()
|
||
nav?.arrSelectedModels.append(contentsOf: models)
|
||
guard ZLPhotoConfiguration.default().showSelectedIndex else {
|
||
return
|
||
}
|
||
resetIndexLabelStatus()
|
||
}
|
||
|
||
private func tapPreviewCell() {
|
||
hideNavView.toggle()
|
||
|
||
let cell = collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0))
|
||
if let cell = cell as? ZLVideoPreviewCell, cell.isPlaying {
|
||
hideNavView = true
|
||
}
|
||
navView.isHidden = hideNavView
|
||
bottomView.isHidden = showBottomViewAndSelectBtn ? hideNavView : true
|
||
}
|
||
|
||
private func showEditImageVC(image: UIImage) {
|
||
let model = arrDataSources[currentIndex]
|
||
let nav = navigationController as? ZLImageNavController
|
||
ZLEditImageViewController.showEditImageVC(parentVC: self, image: image, editModel: model.editImageModel) { [weak self, weak nav] editImage, editImageModel in
|
||
guard let `self` = self else { return }
|
||
model.editImage = editImage
|
||
model.editImageModel = editImageModel
|
||
if nav?.arrSelectedModels.contains(where: { $0 == model }) == false {
|
||
model.isSelected = true
|
||
nav?.arrSelectedModels.append(model)
|
||
self.resetSubViewStatus()
|
||
self.selPhotoPreview?.addSelModel(model: model)
|
||
} else {
|
||
self.selPhotoPreview?.refreshCell(for: model)
|
||
}
|
||
self.collectionView.reloadItems(at: [IndexPath(row: self.currentIndex, section: 0)])
|
||
}
|
||
}
|
||
|
||
private func showEditVideoVC(model: ZLPhotoModel, avAsset: AVAsset) {
|
||
let nav = navigationController as? ZLImageNavController
|
||
let vc = ZLEditVideoViewController(avAsset: avAsset)
|
||
vc.modalPresentationStyle = .fullScreen
|
||
|
||
vc.editFinishBlock = { [weak self, weak nav] url in
|
||
if let url = url {
|
||
ZLPhotoManager.saveVideoToAlbum(url: url) { [weak self, weak nav] suc, asset in
|
||
if suc, asset != nil {
|
||
let m = ZLPhotoModel(asset: asset!)
|
||
nav?.arrSelectedModels.removeAll()
|
||
nav?.arrSelectedModels.append(m)
|
||
nav?.selectImageBlock?()
|
||
} else {
|
||
showAlertView(localLanguageTextValue(.saveVideoError), self)
|
||
}
|
||
}
|
||
} else {
|
||
nav?.arrSelectedModels.removeAll()
|
||
nav?.arrSelectedModels.append(model)
|
||
nav?.selectImageBlock?()
|
||
}
|
||
}
|
||
|
||
present(vc, animated: false, completion: nil)
|
||
}
|
||
}
|
||
|
||
extension ZLPhotoPreviewController: UINavigationControllerDelegate {
|
||
func navigationController(_: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from _: UIViewController, to _: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||
if operation == .push {
|
||
return nil
|
||
}
|
||
|
||
return popInteractiveTransition?.interactive == true ? ZLPhotoPreviewAnimatedTransition() : nil
|
||
}
|
||
|
||
func navigationController(_: UINavigationController, interactionControllerFor _: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||
return popInteractiveTransition?.interactive == true ? popInteractiveTransition : nil
|
||
}
|
||
}
|
||
|
||
// MARK: scroll view delegate
|
||
|
||
extension ZLPhotoPreviewController {
|
||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||
guard scrollView == collectionView else {
|
||
return
|
||
}
|
||
|
||
NotificationCenter.default.post(name: ZLPhotoPreviewController.previewVCScrollNotification, object: nil)
|
||
let offset = scrollView.contentOffset
|
||
var page = Int(round(offset.x / (view.bounds.width + ZLPhotoPreviewController.colItemSpacing)))
|
||
page = max(0, min(page, arrDataSources.count - 1))
|
||
if page == currentIndex {
|
||
return
|
||
}
|
||
currentIndex = page
|
||
resetSubViewStatus()
|
||
selPhotoPreview?.currentShowModelChanged(model: arrDataSources[currentIndex])
|
||
}
|
||
|
||
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||
indexBeforOrientationChanged = currentIndex
|
||
let cell = collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0))
|
||
if let cell = cell as? ZLGifPreviewCell {
|
||
cell.loadGifWhenCellDisplaying()
|
||
} else if let cell = cell as? ZLLivePhotoPreviewCell {
|
||
cell.loadLivePhotoData()
|
||
}
|
||
}
|
||
}
|
||
|
||
extension ZLPhotoPreviewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
|
||
return ZLPhotoPreviewController.colItemSpacing
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
|
||
return ZLPhotoPreviewController.colItemSpacing
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
|
||
return UIEdgeInsets(top: 0, left: ZLPhotoPreviewController.colItemSpacing / 2, bottom: 0, right: ZLPhotoPreviewController.colItemSpacing / 2)
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||
return CGSize(width: view.zl.width, height: view.zl.height)
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||
return arrDataSources.count
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||
let config = ZLPhotoConfiguration.default()
|
||
let model = arrDataSources[indexPath.row]
|
||
|
||
let baseCell: ZLPreviewBaseCell
|
||
|
||
if config.allowSelectGif, model.type == .gif {
|
||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLGifPreviewCell.zl.identifier, for: indexPath) as! ZLGifPreviewCell
|
||
|
||
cell.singleTapBlock = { [weak self] in
|
||
self?.tapPreviewCell()
|
||
}
|
||
|
||
cell.model = model
|
||
|
||
baseCell = cell
|
||
} else if config.allowSelectLivePhoto, model.type == .livePhoto {
|
||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLLivePhotoPreviewCell.zl.identifier, for: indexPath) as! ZLLivePhotoPreviewCell
|
||
|
||
cell.model = model
|
||
|
||
baseCell = cell
|
||
} else if config.allowSelectVideo, model.type == .video {
|
||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLVideoPreviewCell.zl.identifier, for: indexPath) as! ZLVideoPreviewCell
|
||
|
||
cell.model = model
|
||
|
||
baseCell = cell
|
||
} else {
|
||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLPhotoPreviewCell.zl.identifier, for: indexPath) as! ZLPhotoPreviewCell
|
||
|
||
cell.singleTapBlock = { [weak self] in
|
||
self?.tapPreviewCell()
|
||
}
|
||
|
||
cell.model = model
|
||
|
||
baseCell = cell
|
||
}
|
||
|
||
baseCell.singleTapBlock = { [weak self] in
|
||
self?.tapPreviewCell()
|
||
}
|
||
|
||
return baseCell
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
||
(cell as? ZLPreviewBaseCell)?.resetSubViewStatusWhenCellEndDisplay()
|
||
}
|
||
}
|
||
|
||
// MARK: 下方显示的已选择照片列表
|
||
|
||
class ZLPhotoPreviewSelectedView: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDragDelegate, UICollectionViewDropDelegate {
|
||
private lazy var collectionView: UICollectionView = {
|
||
let layout = ZLCollectionViewFlowLayout()
|
||
layout.itemSize = CGSize(width: 60, height: 60)
|
||
layout.minimumLineSpacing = 10
|
||
layout.minimumInteritemSpacing = 10
|
||
layout.scrollDirection = .horizontal
|
||
layout.sectionInset = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15)
|
||
|
||
let view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||
view.backgroundColor = .clear
|
||
view.dataSource = self
|
||
view.delegate = self
|
||
view.showsHorizontalScrollIndicator = false
|
||
view.alwaysBounceHorizontal = true
|
||
ZLPhotoPreviewSelectedViewCell.zl.register(view)
|
||
|
||
if #available(iOS 11.0, *) {
|
||
view.dragDelegate = self
|
||
view.dropDelegate = self
|
||
view.dragInteractionEnabled = true
|
||
view.isSpringLoaded = true
|
||
} else {
|
||
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction))
|
||
view.addGestureRecognizer(longPressGesture)
|
||
}
|
||
|
||
return view
|
||
}()
|
||
|
||
private var arrSelectedModels: [ZLPhotoModel]
|
||
|
||
private var currentShowModel: ZLPhotoModel
|
||
|
||
private var isDraging = false
|
||
|
||
var selectBlock: ((ZLPhotoModel) -> Void)?
|
||
|
||
var endSortBlock: (([ZLPhotoModel]) -> Void)?
|
||
|
||
init(selModels: [ZLPhotoModel], currentShowModel: ZLPhotoModel) {
|
||
arrSelectedModels = selModels
|
||
self.currentShowModel = currentShowModel
|
||
super.init(frame: .zero)
|
||
|
||
setupUI()
|
||
}
|
||
|
||
private func setupUI() {
|
||
addSubview(collectionView)
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder _: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func layoutSubviews() {
|
||
super.layoutSubviews()
|
||
|
||
collectionView.frame = CGRect(x: 0, y: 10, width: bounds.width, height: 80)
|
||
if let index = arrSelectedModels.firstIndex(where: { $0 == self.currentShowModel }) {
|
||
collectionView.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: true)
|
||
}
|
||
}
|
||
|
||
func currentShowModelChanged(model: ZLPhotoModel) {
|
||
guard currentShowModel != model else {
|
||
return
|
||
}
|
||
currentShowModel = model
|
||
|
||
if let index = arrSelectedModels.firstIndex(where: { $0 == self.currentShowModel }) {
|
||
collectionView.performBatchUpdates({
|
||
self.collectionView.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: true)
|
||
}) { _ in
|
||
self.collectionView.reloadItems(at: self.collectionView.indexPathsForVisibleItems)
|
||
}
|
||
} else {
|
||
collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
|
||
}
|
||
}
|
||
|
||
func addSelModel(model: ZLPhotoModel) {
|
||
arrSelectedModels.append(model)
|
||
let indexPath = IndexPath(row: arrSelectedModels.count - 1, section: 0)
|
||
collectionView.insertItems(at: [indexPath])
|
||
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
|
||
}
|
||
|
||
func removeSelModel(model: ZLPhotoModel) {
|
||
guard let index = arrSelectedModels.firstIndex(where: { $0 == model }) else {
|
||
return
|
||
}
|
||
arrSelectedModels.remove(at: index)
|
||
collectionView.deleteItems(at: [IndexPath(row: index, section: 0)])
|
||
}
|
||
|
||
func refreshCell(for model: ZLPhotoModel) {
|
||
guard let index = arrSelectedModels.firstIndex(where: { $0 == model }) else {
|
||
return
|
||
}
|
||
collectionView.reloadItems(at: [IndexPath(row: index, section: 0)])
|
||
}
|
||
|
||
// MARK: iOS10 拖动
|
||
|
||
@objc func longPressAction(_ gesture: UILongPressGestureRecognizer) {
|
||
if gesture.state == .began {
|
||
guard let indexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else {
|
||
return
|
||
}
|
||
isDraging = true
|
||
collectionView.beginInteractiveMovementForItem(at: indexPath)
|
||
} else if gesture.state == .changed {
|
||
collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: collectionView))
|
||
} else if gesture.state == .ended {
|
||
isDraging = false
|
||
collectionView.endInteractiveMovement()
|
||
endSortBlock?(arrSelectedModels)
|
||
} else {
|
||
isDraging = false
|
||
collectionView.cancelInteractiveMovement()
|
||
}
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
|
||
return true
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
|
||
let moveModel = arrSelectedModels[sourceIndexPath.row]
|
||
arrSelectedModels.remove(at: sourceIndexPath.row)
|
||
arrSelectedModels.insert(moveModel, at: destinationIndexPath.row)
|
||
}
|
||
|
||
// MARK: iOS11 拖动
|
||
|
||
@available(iOS 11.0, *)
|
||
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
||
isDraging = true
|
||
let itemProvider = NSItemProvider()
|
||
let item = UIDragItem(itemProvider: itemProvider)
|
||
return [item]
|
||
}
|
||
|
||
@available(iOS 11.0, *)
|
||
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
|
||
if collectionView.hasActiveDrag {
|
||
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
|
||
}
|
||
return UICollectionViewDropProposal(operation: .forbidden)
|
||
}
|
||
|
||
@available(iOS 11.0, *)
|
||
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
|
||
isDraging = false
|
||
guard let destinationIndexPath = coordinator.destinationIndexPath else {
|
||
return
|
||
}
|
||
guard let item = coordinator.items.first else {
|
||
return
|
||
}
|
||
guard let sourceIndexPath = item.sourceIndexPath else {
|
||
return
|
||
}
|
||
|
||
if coordinator.proposal.operation == .move {
|
||
collectionView.performBatchUpdates({
|
||
let moveModel = self.arrSelectedModels[sourceIndexPath.row]
|
||
|
||
self.arrSelectedModels.remove(at: sourceIndexPath.row)
|
||
|
||
self.arrSelectedModels.insert(moveModel, at: destinationIndexPath.row)
|
||
|
||
collectionView.deleteItems(at: [sourceIndexPath])
|
||
collectionView.insertItems(at: [destinationIndexPath])
|
||
}, completion: nil)
|
||
|
||
coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)
|
||
|
||
endSortBlock?(arrSelectedModels)
|
||
}
|
||
}
|
||
|
||
@available(iOS 11.0, *)
|
||
func collectionView(_ collectionView: UICollectionView, dragSessionDidEnd session: UIDragSession) {
|
||
isDraging = false
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||
return arrSelectedModels.count
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLPhotoPreviewSelectedViewCell.zl.identifier, for: indexPath) as! ZLPhotoPreviewSelectedViewCell
|
||
|
||
let m = arrSelectedModels[indexPath.row]
|
||
cell.model = m
|
||
|
||
return cell
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||
guard !isDraging else {
|
||
return
|
||
}
|
||
let m = arrSelectedModels[indexPath.row]
|
||
currentShowModel = m
|
||
collectionView.performBatchUpdates({
|
||
self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
|
||
}) { _ in
|
||
self.collectionView.reloadItems(at: self.collectionView.indexPathsForVisibleItems)
|
||
}
|
||
selectBlock?(m)
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
||
let m = arrSelectedModels[indexPath.row]
|
||
if m == currentShowModel {
|
||
cell.layer.borderWidth = 4
|
||
} else {
|
||
cell.layer.borderWidth = 0
|
||
}
|
||
}
|
||
}
|
||
|
||
class ZLPhotoPreviewSelectedViewCell: UICollectionViewCell {
|
||
private lazy var imageView: UIImageView = {
|
||
let view = UIImageView()
|
||
view.contentMode = .scaleAspectFill
|
||
view.clipsToBounds = true
|
||
return view
|
||
}()
|
||
|
||
private lazy var tagImageView: UIImageView = {
|
||
let view = UIImageView()
|
||
view.contentMode = .scaleAspectFit
|
||
view.clipsToBounds = true
|
||
return view
|
||
}()
|
||
|
||
private lazy var tagLabel: UILabel = {
|
||
let label = UILabel()
|
||
label.font = .zl.font(ofSize: 13)
|
||
label.textColor = .white
|
||
return label
|
||
}()
|
||
|
||
private var imageRequestID: PHImageRequestID = PHInvalidImageRequestID
|
||
|
||
private var imageIdentifier = ""
|
||
|
||
var model: ZLPhotoModel! {
|
||
didSet {
|
||
self.configureCell()
|
||
}
|
||
}
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
|
||
layer.borderColor = UIColor.zl.bottomToolViewBtnNormalBgColorOfPreviewVC.cgColor
|
||
|
||
contentView.addSubview(imageView)
|
||
contentView.addSubview(tagImageView)
|
||
contentView.addSubview(tagLabel)
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder _: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func layoutSubviews() {
|
||
super.layoutSubviews()
|
||
imageView.frame = bounds
|
||
tagImageView.frame = CGRect(x: 5, y: bounds.height - 25, width: 20, height: 20)
|
||
tagLabel.frame = CGRect(x: 5, y: bounds.height - 25, width: bounds.width - 10, height: 20)
|
||
}
|
||
|
||
private func configureCell() {
|
||
let size = CGSize(width: bounds.width * 1.5, height: bounds.height * 1.5)
|
||
|
||
if imageRequestID > PHInvalidImageRequestID {
|
||
PHImageManager.default().cancelImageRequest(imageRequestID)
|
||
}
|
||
|
||
if model.type == .video {
|
||
tagImageView.isHidden = false
|
||
tagImageView.image = .zl.getImage("zl_video")
|
||
tagLabel.isHidden = true
|
||
} else if ZLPhotoConfiguration.default().allowSelectGif, model.type == .gif {
|
||
tagImageView.isHidden = true
|
||
tagLabel.isHidden = false
|
||
tagLabel.text = "GIF"
|
||
} else if ZLPhotoConfiguration.default().allowSelectLivePhoto, model.type == .livePhoto {
|
||
tagImageView.isHidden = false
|
||
tagImageView.image = .zl.getImage("zl_livePhoto")
|
||
tagLabel.isHidden = true
|
||
} else {
|
||
if let _ = model.editImage {
|
||
tagImageView.isHidden = false
|
||
tagImageView.image = .zl.getImage("zl_editImage_tag")
|
||
} else {
|
||
tagImageView.isHidden = true
|
||
tagLabel.isHidden = true
|
||
}
|
||
}
|
||
|
||
imageIdentifier = model.ident
|
||
imageView.image = nil
|
||
|
||
if let ei = model.editImage {
|
||
imageView.image = ei
|
||
} else {
|
||
imageRequestID = ZLPhotoManager.fetchImage(for: model.asset, size: size, completion: { [weak self] image, _ in
|
||
if self?.imageIdentifier == self?.model.ident {
|
||
self?.imageView.image = image
|
||
}
|
||
})
|
||
}
|
||
}
|
||
}
|