| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116 |
- //
- // ZLPhotoPreviewCell.swift
- // ZLPhotoBrowser
- //
- // Created by long on 2020/8/21.
- //
- // 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
- import PhotosUI
- class ZLPreviewBaseCell: UICollectionViewCell {
-
- var singleTapBlock: ( () -> Void )?
-
- var currentImage: UIImage? {
- return nil
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- NotificationCenter.default.addObserver(self, selector: #selector(previewVCScroll), name: ZLPhotoPreviewController.previewVCScrollNotification, object: nil)
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- @objc func previewVCScroll() {
-
- }
-
- func resetSubViewStatusWhenCellEndDisplay() {
-
- }
-
- func resizeImageView(imageView: UIImageView, asset: PHAsset) {
- let size = CGSize(width: asset.pixelWidth, height: asset.pixelHeight)
- var frame: CGRect = .zero
-
- let viewW = self.bounds.width
- let viewH = self.bounds.height
-
- var width = viewW
-
- // video和livephoto没必要处理长图和宽图
- if UIApplication.shared.statusBarOrientation.isLandscape {
- let height = viewH
- frame.size.height = height
-
- let imageWHRatio = size.width / size.height
- let viewWHRatio = viewW / viewH
-
- if imageWHRatio > viewWHRatio {
- frame.size.width = floor(height * imageWHRatio)
- if frame.size.width > viewW {
- frame.size.width = viewW
- frame.size.height = viewW / imageWHRatio
- }
- } else {
- width = floor(height * imageWHRatio)
- if width < 1 || width.isNaN {
- width = viewW
- }
- frame.size.width = width
- }
- } else {
- frame.size.width = width
-
- let imageHWRatio = size.height / size.width
- let viewHWRatio = viewH / viewW
-
- if imageHWRatio > viewHWRatio {
- frame.size.height = floor(width * imageHWRatio)
- } else {
- var height = floor(width * imageHWRatio)
- if height < 1 || height.isNaN {
- height = viewH
- }
- frame.size.height = height
- }
- }
-
- imageView.frame = frame
-
- if UIApplication.shared.statusBarOrientation.isLandscape {
- if frame.height < viewH {
- imageView.center = CGPoint(x: viewW / 2, y: viewH / 2)
- } else {
- imageView.frame = CGRect(origin: CGPoint(x: (viewW-frame.width)/2, y: 0), size: frame.size)
- }
- } else {
- if frame.width < viewW || frame.height < viewH {
- imageView.center = CGPoint(x: viewW / 2, y: viewH / 2)
- }
- }
- }
-
- func animateImageFrame(convertTo view: UIView) -> CGRect {
- return .zero
- }
-
- }
- // MARK: local and net image preview cell
- class ZLLocalImagePreviewCell: ZLPreviewBaseCell {
-
- override var currentImage: UIImage? {
- return self.preview.image
- }
-
- var preview: ZLPreviewView!
-
- var image: UIImage? = nil {
- didSet {
- self.preview.imageView.image = image
- self.preview.resetSubViewSize()
- }
- }
-
- var longPressBlock: ( () -> Void )?
-
- deinit {
- zl_debugPrint("ZLLocalImagePreviewCell deinit")
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- self.setupUI()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func layoutSubviews() {
- super.layoutSubviews()
- self.preview.frame = self.bounds
- }
-
- private func setupUI() {
- self.preview = ZLPreviewView()
- self.preview.singleTapBlock = { [weak self] in
- self?.singleTapBlock?()
- }
- self.contentView.addSubview(self.preview)
-
- let longGes = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:)))
- longGes.minimumPressDuration = 0.5
- self.addGestureRecognizer(longGes)
- }
-
- override func resetSubViewStatusWhenCellEndDisplay() {
- self.preview.scrollView.zoomScale = 1
- }
-
- @objc func longPressAction(_ ges: UILongPressGestureRecognizer) {
- guard let _ = self.currentImage else {
- return
- }
-
- if ges.state == .began {
- self.longPressBlock?()
- }
- }
-
- }
- // MARK: net image preview cell
- class ZLNetImagePreviewCell: ZLLocalImagePreviewCell {
-
- var progressView: ZLProgressView!
-
- var progress: CGFloat = 0 {
- didSet {
- progressView.progress = progress
- progressView.isHidden = progress >= 1
- }
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- self.progressView = ZLProgressView()
- self.progressView.isHidden = true
- self.contentView.addSubview(self.progressView)
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func layoutSubviews() {
- super.layoutSubviews()
-
- self.bringSubviewToFront(self.progressView)
- self.progressView.frame = CGRect(x: self.bounds.width / 2 - 20, y: self.bounds.height / 2 - 20, width: 40, height: 40)
- }
-
- override func resetSubViewStatusWhenCellEndDisplay() {
- self.progressView.isHidden = true
- self.preview.scrollView.zoomScale = 1
- }
-
- }
- // MARK: static image preview cell
- class ZLPhotoPreviewCell: ZLPreviewBaseCell {
-
- override var currentImage: UIImage? {
- return self.preview.image
- }
-
- var preview: ZLPreviewView!
-
- var model: ZLPhotoModel! {
- didSet {
- self.preview.model = self.model
- }
- }
-
- deinit {
- zl_debugPrint("ZLPhotoPreviewCell deinit")
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- self.setupUI()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func layoutSubviews() {
- super.layoutSubviews()
- self.preview.frame = self.bounds
- }
-
- private func setupUI() {
- self.preview = ZLPreviewView()
- self.preview.singleTapBlock = { [weak self] in
- self?.singleTapBlock?()
- }
- self.contentView.addSubview(self.preview)
- }
-
- override func resetSubViewStatusWhenCellEndDisplay() {
- self.preview.scrollView.zoomScale = 1
- }
-
- override func animateImageFrame(convertTo view: UIView) -> CGRect {
- let r1 = self.preview.scrollView.convert(self.preview.containerView.frame, to: self)
- return self.convert(r1, to: view)
- }
-
- }
- // MARK: gif preview cell
- class ZLGifPreviewCell: ZLPreviewBaseCell {
-
- override var currentImage: UIImage? {
- return self.preview.image
- }
-
- var preview: ZLPreviewView!
-
- var model: ZLPhotoModel! {
- didSet {
- self.preview.model = self.model
- }
- }
-
- deinit {
- zl_debugPrint("ZLGifPreviewCell deinit")
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- self.setupUI()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func layoutSubviews() {
- super.layoutSubviews()
- self.preview.frame = self.bounds
- }
-
- private func setupUI() {
- self.preview = ZLPreviewView()
- self.preview.singleTapBlock = { [weak self] in
- self?.singleTapBlock?()
- }
- self.contentView.addSubview(self.preview)
- }
-
- override func previewVCScroll() {
- self.preview.pauseGif()
- }
-
- func resumeGif() {
- self.preview.resumeGif()
- }
-
- func pauseGif() {
- self.preview.pauseGif()
- }
-
- /// gif图加载会导致主线程卡顿一下,所以放在willdisplay时候加载
- func loadGifWhenCellDisplaying() {
- self.preview.loadGifData()
- }
-
- override func resetSubViewStatusWhenCellEndDisplay() {
- self.preview.scrollView.zoomScale = 1
- }
-
- override func animateImageFrame(convertTo view: UIView) -> CGRect {
- let r1 = self.preview.scrollView.convert(self.preview.containerView.frame, to: self)
- return self.convert(r1, to: view)
- }
-
- }
- // MARK: live photo preview cell
- class ZLLivePhotoPreviewCell: ZLPreviewBaseCell {
-
- override var currentImage: UIImage? {
- return self.imageView.image
- }
-
- var livePhotoView: PHLivePhotoView!
-
- var imageView: UIImageView!
-
- var imageRequestID: PHImageRequestID = PHInvalidImageRequestID
-
- var livePhotoRequestID: PHImageRequestID = PHInvalidImageRequestID
-
- var onFetchingLivePhoto = false
-
- var fetchLivePhotoDone = false
-
- var model: ZLPhotoModel! {
- didSet {
- self.loadNormalImage()
- }
- }
-
- deinit {
- zl_debugPrint("ZLLivePhotoPewviewCell deinit")
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- self.setupUI()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func layoutSubviews() {
- super.layoutSubviews()
- self.livePhotoView.frame = self.bounds
- self.resizeImageView(imageView: self.imageView, asset: self.model.asset)
- }
-
- private func setupUI() {
- self.livePhotoView = PHLivePhotoView()
- self.livePhotoView.contentMode = .scaleAspectFit
- self.contentView.addSubview(self.livePhotoView)
-
- self.imageView = UIImageView()
- self.imageView.contentMode = .scaleAspectFit
- self.contentView.addSubview(self.imageView)
- }
-
- override func previewVCScroll() {
- self.livePhotoView.stopPlayback()
- }
-
- func loadNormalImage() {
- if self.imageRequestID > PHInvalidImageRequestID {
- PHImageManager.default().cancelImageRequest(self.imageRequestID)
- }
- if self.livePhotoRequestID > PHInvalidImageRequestID {
- PHImageManager.default().cancelImageRequest(self.livePhotoRequestID)
- }
- self.onFetchingLivePhoto = false
- self.imageView.isHidden = false
-
- // livephoto 加载个较小的预览图即可
- var size = self.model.previewSize
- size.width /= 4
- size.height /= 4
-
- self.resizeImageView(imageView: self.imageView, asset: self.model.asset)
- self.imageRequestID = ZLPhotoManager.fetchImage(for: self.model.asset, size: size, completion: { [weak self] (image, isDegread) in
- self?.imageView.image = image
- })
- }
-
- func loadLivePhotoData() {
- guard !self.onFetchingLivePhoto else {
- if self.fetchLivePhotoDone {
- self.startPlayLivePhoto()
- }
- return
- }
- self.onFetchingLivePhoto = true
- self.fetchLivePhotoDone = false
-
- self.livePhotoRequestID = ZLPhotoManager.fetchLivePhoto(for: self.model.asset, completion: { (livePhoto, info, isDegraded) in
- if !isDegraded {
- self.fetchLivePhotoDone = true
- self.livePhotoView.livePhoto = livePhoto
- self.startPlayLivePhoto()
- }
- })
- }
-
- func startPlayLivePhoto() {
- self.imageView.isHidden = true
- self.livePhotoView.startPlayback(with: .full)
- }
-
- override func animateImageFrame(convertTo view: UIView) -> CGRect {
- return self.convert(self.imageView.frame, to: view)
- }
-
- }
- // MARK: video preview cell
- class ZLVideoPreviewCell: ZLPreviewBaseCell {
-
- override var currentImage: UIImage? {
- return self.imageView.image
- }
-
- var player: AVPlayer?
-
- var playerLayer: AVPlayerLayer?
-
- var progressView: ZLProgressView!
-
- var imageView: UIImageView!
-
- var playBtn: UIButton!
-
- var syncErrorLabel: UILabel!
-
- var imageRequestID: PHImageRequestID = PHInvalidImageRequestID
-
- var videoRequestID: PHImageRequestID = PHInvalidImageRequestID
-
- var onFetchingVideo = false
-
- var fetchVideoDone = false
-
- var isPlaying: Bool {
- if self.player != nil, self.player?.rate != 0 {
- return true
- }
- return false
- }
-
- var model: ZLPhotoModel! {
- didSet {
- self.configureCell()
- }
- }
-
- deinit {
- zl_debugPrint("ZLVideoPreviewCell deinit")
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- self.setupUI()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func layoutSubviews() {
- super.layoutSubviews()
- self.playerLayer?.frame = self.bounds
- self.resizeImageView(imageView: self.imageView, asset: self.model.asset)
- let insets = deviceSafeAreaInsets()
- self.playBtn.frame = CGRect(x: 0, y: insets.top, width: self.bounds.width, height: self.bounds.height - insets.top - insets.bottom)
- self.syncErrorLabel.frame = CGRect(x: 10, y: insets.top + 60, width: self.bounds.width - 20, height: 35)
- self.progressView.frame = CGRect(x: self.bounds.width / 2 - 30, y: self.bounds.height / 2 - 30, width: 60, height: 60)
- }
-
- private func setupUI() {
- self.imageView = UIImageView()
- self.imageView.clipsToBounds = true
- self.imageView.contentMode = .scaleAspectFill
- self.contentView.addSubview(self.imageView)
-
- let attStr = NSMutableAttributedString()
- let attach = NSTextAttachment()
- attach.image = getImage("zl_videoLoadFailed")
- attach.bounds = CGRect(x: 0, y: -10, width: 30, height: 30)
- attStr.append(NSAttributedString(attachment: attach))
- let errorText = NSAttributedString(string: localLanguageTextValue(.iCloudVideoLoadFaild), attributes: [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: getFont(12)])
- attStr.append(errorText)
- self.syncErrorLabel = UILabel()
- self.syncErrorLabel.attributedText = attStr
- self.contentView.addSubview(self.syncErrorLabel)
-
- self.progressView = ZLProgressView()
- self.contentView.addSubview(self.progressView)
-
- self.playBtn = UIButton(type: .custom)
- self.playBtn.setImage(getImage("zl_playVideo"), for: .normal)
- self.playBtn.addTarget(self, action: #selector(playBtnClick), for: .touchUpInside)
- self.contentView.addSubview(self.playBtn)
-
- NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActive), name: UIApplication.willResignActiveNotification, object: nil)
- }
-
- func configureCell() {
- self.imageView.image = nil
- self.imageView.isHidden = false
- self.syncErrorLabel.isHidden = true
- self.playBtn.isEnabled = false
- self.player = nil
- self.playerLayer?.removeFromSuperlayer()
- self.playerLayer = nil
-
- if self.imageRequestID > PHInvalidImageRequestID {
- PHImageManager.default().cancelImageRequest(self.imageRequestID)
- }
- if self.videoRequestID > PHInvalidImageRequestID {
- PHImageManager.default().cancelImageRequest(self.videoRequestID)
- }
-
- // 视频预览图尺寸
- var size = self.model.previewSize
- size.width /= 2
- size.height /= 2
-
- self.resizeImageView(imageView: self.imageView, asset: self.model.asset)
- self.imageRequestID = ZLPhotoManager.fetchImage(for: self.model.asset, size: size, completion: { (image, isDegraded) in
- self.imageView.image = image
- })
-
- self.videoRequestID = ZLPhotoManager.fetchVideo(for: self.model.asset, progress: { [weak self] (progress, _, _, _) in
- self?.progressView.progress = progress
- zl_debugPrint("video progress \(progress)")
- if progress >= 1 {
- zl_debugPrint("video load finished")
- self?.progressView.isHidden = true
- } else {
- self?.progressView.isHidden = false
- }
- }, completion: { [weak self] (item, info, isDegraded) in
- let error = info?[PHImageErrorKey] as? Error
- let isFetchError = ZLPhotoManager.isFetchImageError(error)
- if isFetchError {
- self?.syncErrorLabel.isHidden = false
- self?.playBtn.setImage(nil, for: .normal)
- }
- if !isDegraded, item != nil {
- self?.fetchVideoDone = true
- self?.configurePlayerLayer(item!)
- }
- })
- }
-
- func configurePlayerLayer(_ item: AVPlayerItem) {
- self.playBtn.setImage(getImage("zl_playVideo"), for: .normal)
- self.playBtn.isEnabled = true
-
- self.player = AVPlayer(playerItem: item)
- self.playerLayer = AVPlayerLayer(player: self.player)
- self.playerLayer?.frame = self.bounds
- self.layer.insertSublayer(self.playerLayer!, at: 0)
- NotificationCenter.default.addObserver(self, selector: #selector(playFinish), name: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem)
- }
-
- @objc func playBtnClick() {
- let currentTime = self.player?.currentItem?.currentTime()
- let duration = self.player?.currentItem?.duration
- if self.player?.rate == 0 {
- if currentTime?.value == duration?.value {
- self.player?.currentItem?.seek(to: CMTimeMake(value: 0, timescale: 1))
- }
- self.imageView.isHidden = true
- self.player?.play()
- self.playBtn.setImage(nil, for: .normal)
- self.singleTapBlock?()
- } else {
- self.pausePlayer(seekToZero: false)
- }
- }
-
- @objc func playFinish() {
- self.pausePlayer(seekToZero: true)
- }
-
- @objc func appWillResignActive() {
- if self.player != nil, self.player?.rate != 0 {
- self.pausePlayer(seekToZero: false)
- }
- }
-
- override func previewVCScroll() {
- if self.player != nil, self.player?.rate != 0 {
- self.pausePlayer(seekToZero: false)
- }
- }
-
- override func resetSubViewStatusWhenCellEndDisplay() {
- self.imageView.isHidden = false
- self.player?.currentItem?.seek(to: CMTimeMake(value: 0, timescale: 1))
- }
-
- func pausePlayer(seekToZero: Bool) {
- self.player?.pause()
- if seekToZero {
- self.player?.seek(to: .zero)
- }
- self.playBtn.setImage(getImage("zl_playVideo"), for: .normal)
- self.singleTapBlock?()
- }
-
- func pauseWhileTransition() {
- self.player?.pause()
- self.playBtn.setImage(getImage("zl_playVideo"), for: .normal)
- }
-
- override func animateImageFrame(convertTo view: UIView) -> CGRect {
- return self.convert(self.imageView.frame, to: view)
- }
-
- }
- // MARK: net video preview cell
- class ZLNetVideoPreviewCell: ZLPreviewBaseCell {
-
- var player: AVPlayer?
-
- var playerLayer: AVPlayerLayer?
-
- var playBtn: UIButton!
-
- var isPlaying: Bool {
- if self.player != nil, self.player?.rate != 0 {
- return true
- }
- return false
- }
-
- var videoUrl: URL! {
- didSet {
- self.configureCell()
- }
- }
-
- deinit {
- zl_debugPrint("v deinit")
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- self.setupUI()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func layoutSubviews() {
- super.layoutSubviews()
- self.playerLayer?.frame = self.bounds
- let insets = deviceSafeAreaInsets()
- self.playBtn.frame = CGRect(x: 0, y: insets.top, width: self.bounds.width, height: self.bounds.height - insets.top - insets.bottom)
- }
-
- private func setupUI() {
- self.playBtn = UIButton(type: .custom)
- self.playBtn.setImage(getImage("zl_playVideo"), for: .normal)
- self.playBtn.addTarget(self, action: #selector(playBtnClick), for: .touchUpInside)
- self.contentView.addSubview(self.playBtn)
-
- NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActive), name: UIApplication.willResignActiveNotification, object: nil)
- }
-
- func configureCell() {
- self.player = nil
- self.playerLayer?.removeFromSuperlayer()
- self.playerLayer = nil
-
- self.player = AVPlayer(playerItem: AVPlayerItem(url: self.videoUrl))
- self.playerLayer = AVPlayerLayer(player: self.player)
- self.playerLayer?.frame = self.bounds
- self.layer.insertSublayer(self.playerLayer!, at: 0)
- NotificationCenter.default.addObserver(self, selector: #selector(playFinish), name: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem)
- }
-
- @objc func playBtnClick() {
- let currentTime = self.player?.currentItem?.currentTime()
- let duration = self.player?.currentItem?.duration
- if self.player?.rate == 0 {
- if currentTime?.value == duration?.value {
- self.player?.currentItem?.seek(to: CMTimeMake(value: 0, timescale: 1))
- }
- self.player?.play()
- self.playBtn.setImage(nil, for: .normal)
- self.singleTapBlock?()
- } else {
- self.pausePlayer(seekToZero: false)
- }
- }
-
- @objc func playFinish() {
- self.pausePlayer(seekToZero: true)
- }
-
- @objc func appWillResignActive() {
- if self.player != nil, self.player?.rate != 0 {
- self.pausePlayer(seekToZero: false)
- }
- }
-
- override func previewVCScroll() {
- if self.player != nil, self.player?.rate != 0 {
- self.pausePlayer(seekToZero: false)
- }
- }
-
- override func resetSubViewStatusWhenCellEndDisplay() {
- self.player?.currentItem?.seek(to: CMTimeMake(value: 0, timescale: 1))
- }
-
- func pausePlayer(seekToZero: Bool) {
- self.player?.pause()
- if seekToZero {
- self.player?.seek(to: .zero)
- }
- self.playBtn.setImage(getImage("zl_playVideo"), for: .normal)
- self.singleTapBlock?()
- }
-
- }
- // MARK: class ZLPreviewView
- class ZLPreviewView: UIView {
-
- static let defaultMaxZoomScale: CGFloat = 3
-
- var scrollView: UIScrollView!
-
- var containerView: UIView!
-
- var imageView: UIImageView!
-
- var image: UIImage? {
- self.imageView.image
- }
-
- var progressView: ZLProgressView!
-
- var singleTapBlock: ( () -> Void )?
-
- var doubleTapBlock: ( () -> Void )?
-
- var imageRequestID: PHImageRequestID = PHInvalidImageRequestID
-
- var gifImageRequestID: PHImageRequestID = PHInvalidImageRequestID
-
- var imageIdentifier: String = ""
-
- var onFetchingGif = false
-
- var fetchGifDone = false
-
- var model: ZLPhotoModel! {
- didSet {
- self.configureView()
- }
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- self.setupUI()
-
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func layoutSubviews() {
- super.layoutSubviews()
- self.scrollView.frame = self.bounds
- self.progressView.frame = CGRect(x: self.bounds.width / 2 - 20, y: self.bounds.height / 2 - 20, width: 40, height: 40)
- self.scrollView.zoomScale = 1
- self.resetSubViewSize()
- }
-
- func setupUI() {
- self.scrollView = UIScrollView()
- self.scrollView.maximumZoomScale = ZLPreviewView.defaultMaxZoomScale
- self.scrollView.minimumZoomScale = 1
- self.scrollView.isMultipleTouchEnabled = true
- self.scrollView.delegate = self
- self.scrollView.showsHorizontalScrollIndicator = false
- self.scrollView.showsVerticalScrollIndicator = false
- self.scrollView.delaysContentTouches = false
- self.addSubview(self.scrollView)
-
- self.containerView = UIView()
- self.scrollView.addSubview(self.containerView)
-
- self.imageView = UIImageView()
- self.imageView.contentMode = .scaleAspectFill
- self.imageView.clipsToBounds = true
- self.containerView.addSubview(self.imageView)
-
- self.progressView = ZLProgressView()
- self.addSubview(self.progressView)
-
- let singleTap = UITapGestureRecognizer(target: self, action: #selector(singleTapAction(_:)))
- self.addGestureRecognizer(singleTap)
-
- let doubleTap = UITapGestureRecognizer(target: self, action: #selector(doubleTapAction(_:)))
- doubleTap.numberOfTapsRequired = 2
- self.addGestureRecognizer(doubleTap)
-
- singleTap.require(toFail: doubleTap)
- }
-
- @objc func singleTapAction(_ tap: UITapGestureRecognizer) {
- self.singleTapBlock?()
- }
-
- @objc func doubleTapAction(_ tap: UITapGestureRecognizer) {
- let scale: CGFloat = self.scrollView.zoomScale != self.scrollView.maximumZoomScale ? self.scrollView.maximumZoomScale : 1
- let tapPoint = tap.location(in: self)
- var rect = CGRect.zero
- rect.size.width = self.scrollView.frame.width / scale
- rect.size.height = self.scrollView.frame.height / scale
- rect.origin.x = tapPoint.x - (rect.size.width / 2)
- rect.origin.y = tapPoint.y - (rect.size.height / 2)
- self.scrollView.zoom(to: rect, animated: true)
- }
-
- func configureView() {
- if self.imageRequestID > PHInvalidImageRequestID {
- PHImageManager.default().cancelImageRequest(self.imageRequestID)
- }
- if self.gifImageRequestID > PHInvalidImageRequestID {
- PHImageManager.default().cancelImageRequest(self.gifImageRequestID)
- }
-
- self.scrollView.zoomScale = 1
- self.imageIdentifier = self.model.ident
-
- if ZLPhotoConfiguration.default().allowSelectGif, self.model.type == .gif {
- self.loadGifFirstFrame()
- } else {
- self.loadPhoto()
- }
- }
-
- func requestPhotoSize(gif: Bool) -> CGSize {
- // gif 情况下优先加载一个小的缩略图
- var size = self.model.previewSize
- if gif {
- size.width /= 2
- size.height /= 2
- }
- return size
- }
-
- func loadPhoto() {
- if let editImage = self.model.editImage {
- self.imageView.image = editImage
- self.resetSubViewSize()
- } else {
- self.imageRequestID = ZLPhotoManager.fetchImage(for: self.model.asset, size: self.requestPhotoSize(gif: false), progress: { [weak self] (progress, _, _, _) in
- self?.progressView.progress = progress
- if progress >= 1 {
- self?.progressView.isHidden = true
- } else {
- self?.progressView.isHidden = false
- }
- }, completion: { [weak self] (image, isDegraded) in
- guard self?.imageIdentifier == self?.model.ident else {
- return
- }
- self?.imageView.image = image
- self?.resetSubViewSize()
- if !isDegraded {
- self?.progressView.isHidden = true
- self?.imageRequestID = PHInvalidImageRequestID
- }
- })
- }
- }
-
- func loadGifFirstFrame() {
- self.onFetchingGif = false
- self.fetchGifDone = false
-
- self.imageRequestID = ZLPhotoManager.fetchImage(for: self.model.asset, size: self.requestPhotoSize(gif: true), completion: { [weak self] (image, isDegraded) in
- guard self?.imageIdentifier == self?.model.ident else {
- return
- }
- if self?.fetchGifDone == false {
- self?.imageView.image = image
- self?.resetSubViewSize()
- }
- })
- }
-
- func loadGifData() {
- guard !self.onFetchingGif else {
- if self.fetchGifDone {
- self.resumeGif()
- }
- return
- }
- self.onFetchingGif = true
- self.fetchGifDone = false
- self.imageView.layer.speed = 1
- self.imageView.layer.timeOffset = 0
- self.imageView.layer.beginTime = 0
- self.gifImageRequestID = ZLPhotoManager.fetchOriginalImageData(for: self.model.asset, progress: { [weak self] (progress, _, _, _) in
- self?.progressView.progress = progress
- if progress >= 1 {
- self?.progressView.isHidden = true
- } else {
- self?.progressView.isHidden = false
- }
- }, completion: { [weak self] (data, _, isDegraded) in
- guard self?.imageIdentifier == self?.model.ident else {
- return
- }
- if !isDegraded {
- self?.fetchGifDone = true
- self?.imageView.image = UIImage.zl_animateGifImage(data: data)
- self?.resetSubViewSize()
- }
- })
- }
-
- func resetSubViewSize() {
- let size: CGSize
- if let _ = self.model {
- if let ei = self.model.editImage {
- size = ei.size
- } else {
- size = CGSize(width: self.model.asset.pixelWidth, height: self.model.asset.pixelHeight)
- }
- } else {
- size = self.imageView.image?.size ?? self.bounds.size
- }
-
- var frame: CGRect = .zero
-
- let viewW = self.bounds.width
- let viewH = self.bounds.height
-
- var width = viewW
-
- if UIApplication.shared.statusBarOrientation.isLandscape {
- let height = viewH
- frame.size.height = height
-
- let imageWHRatio = size.width / size.height
- let viewWHRatio = viewW / viewH
-
- if imageWHRatio > viewWHRatio {
- frame.size.width = floor(height * imageWHRatio)
- if frame.size.width > viewW {
- // 宽图
- frame.size.width = viewW
- frame.size.height = viewW / imageWHRatio
- }
- } else {
- width = floor(height * imageWHRatio)
- if width < 1 || width.isNaN {
- width = viewW
- }
- frame.size.width = width
- }
- } else {
- frame.size.width = width
-
- let imageHWRatio = size.height / size.width
- let viewHWRatio = viewH / viewW
-
- if imageHWRatio > viewHWRatio {
- // 长图
- frame.size.width = min(size.width, viewW)
- frame.size.height = floor(frame.size.width * imageHWRatio)
- } else {
- var height = floor(frame.size.width * imageHWRatio)
- if height < 1 || height.isNaN {
- height = viewH
- }
- frame.size.height = height
- }
- }
-
- // 优化 scroll view zoom scale
- if frame.width < frame.height {
- self.scrollView.maximumZoomScale = max(ZLPreviewView.defaultMaxZoomScale, viewW / frame.width)
- } else {
- self.scrollView.maximumZoomScale = max(ZLPreviewView.defaultMaxZoomScale, viewH / frame.height)
- }
-
- self.containerView.frame = frame
-
- var contenSize: CGSize = .zero
- if UIApplication.shared.statusBarOrientation.isLandscape {
- contenSize = CGSize(width: width, height: max(viewH, frame.height))
- if frame.height < viewH {
- self.containerView.center = CGPoint(x: viewW / 2, y: viewH / 2)
- } else {
- self.containerView.frame = CGRect(origin: CGPoint(x: (viewW-frame.width)/2, y: 0), size: frame.size)
- }
- } else {
- contenSize = frame.size
- if frame.width < viewW || frame.height < viewH {
- self.containerView.center = CGPoint(x: viewW / 2, y: viewH / 2)
- }
- }
-
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
- self.scrollView.contentSize = contenSize
- self.imageView.frame = self.containerView.bounds
- self.scrollView.contentOffset = .zero
- }
- }
-
- func resumeGif() {
- guard let m = self.model else { return }
- guard ZLPhotoConfiguration.default().allowSelectGif && m.type == .gif else { return }
- guard self.imageView.layer.speed != 1 else { return }
-
- let pauseTime = self.imageView.layer.timeOffset
- self.imageView.layer.speed = 1
- self.imageView.layer.timeOffset = 0
- self.imageView.layer.beginTime = 0
- let timeSincePause = self.imageView.layer.convertTime(CACurrentMediaTime(), from: nil) - pauseTime
- self.imageView.layer.beginTime = timeSincePause
- }
-
- func pauseGif() {
- guard let m = self.model else { return }
- guard ZLPhotoConfiguration.default().allowSelectGif && m.type == .gif else { return }
- guard self.imageView.layer.speed != 0 else { return }
-
- let pauseTime = self.imageView.layer.convertTime(CACurrentMediaTime(), from: nil)
- self.imageView.layer.speed = 0
- self.imageView.layer.timeOffset = pauseTime
- }
-
- }
- extension ZLPreviewView: UIScrollViewDelegate {
-
- func viewForZooming(in scrollView: UIScrollView) -> UIView? {
- return self.containerView
- }
-
- func scrollViewDidZoom(_ scrollView: UIScrollView) {
- let offsetX = (scrollView.frame.width > scrollView.contentSize.width) ? (scrollView.frame.width - scrollView.contentSize.width) * 0.5 : 0
- let offsetY = (scrollView.frame.height > scrollView.contentSize.height) ? (scrollView.frame.height - scrollView.contentSize.height) * 0.5 : 0
- self.containerView.center = CGPoint(x: scrollView.contentSize.width * 0.5 + offsetX, y: scrollView.contentSize.height * 0.5 + offsetY)
- }
-
- func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
- self.resumeGif()
- }
-
- }
|