Commit 5c1b463a authored by 刘俊宏's avatar 刘俊宏

添加JXPhotoBrowser(图片浏览器)

parent 31ddb37d
This diff is collapsed.
//
// JXPhotoBrowserAnimatedTransitioning.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
public protocol JXPhotoBrowserAnimatedTransitioning: UIViewControllerAnimatedTransitioning {
var isForShow: Bool { get set }
var photoBrowser: JXPhotoBrowser? { get set }
var isNavigationAnimation: Bool { get set }
}
private var isForShowKey = "isForShowKey"
private var photoBrowserKey = "photoBrowserKey"
extension JXPhotoBrowserAnimatedTransitioning {
public var isForShow: Bool {
get {
if let value = objc_getAssociatedObject(self, &isForShowKey) as? Bool {
return value
}
return true
}
set {
objc_setAssociatedObject(self, &isForShowKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
public weak var photoBrowser: JXPhotoBrowser? {
get {
return objc_getAssociatedObject(self, &photoBrowserKey) as? JXPhotoBrowser
}
set {
objc_setAssociatedObject(self, &photoBrowserKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
public var isNavigationAnimation: Bool {
get { return false }
set { }
}
public func fastSnapshot(with view: UIView) -> UIView? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImageView(image: image)
}
public func snapshot(with view: UIView) -> UIView? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
view.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImageView(image: image)
}
}
//
// JXPhotoBrowserCell.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/26.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
public protocol JXPhotoBrowserCell: UIView {
static func generate(with browser: JXPhotoBrowser) -> Self
}
//
// JXPhotoBrowserDefaultPageIndicator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
open class JXPhotoBrowserDefaultPageIndicator: UIPageControl, JXPhotoBrowserPageIndicator {
/// 页码与底部的距离
open lazy var bottomPadding: CGFloat = {
if #available(iOS 11.0, *),
let window = UIApplication.shared.keyWindow,
window.safeAreaInsets.bottom > 0 {
return 20
}
return 15
}()
open func setup(with browser: JXPhotoBrowser) {
isEnabled = false
}
open func reloadData(numberOfItems: Int, pageIndex: Int) {
numberOfPages = numberOfItems
currentPage = min(pageIndex, numberOfPages - 1)
sizeToFit()
isHidden = numberOfPages <= 1
if let view = superview {
center.x = view.bounds.width / 2
frame.origin.y = view.bounds.maxY - bottomPadding - bounds.height
}
}
open func didChanged(pageIndex: Int) {
currentPage = pageIndex
}
}
//
// JXPhotoBrowserFadeAnimator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
open class JXPhotoBrowserFadeAnimator: NSObject, JXPhotoBrowserAnimatedTransitioning {
open var showDuration: TimeInterval = 0.25
open var dismissDuration: TimeInterval = 0.25
public var isNavigationAnimation: Bool = false
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return isForShow ? showDuration : dismissDuration
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return
}
if isNavigationAnimation, isForShow,
let fromView = transitionContext.view(forKey: .from),
let fromViewSnapshot = snapshot(with: fromView),
let toView = transitionContext.view(forKey: .to) {
toView.insertSubview(fromViewSnapshot, at: 0)
}
if isForShow {
browser.maskView.alpha = 0
browser.browserView.alpha = 0
if let toView = transitionContext.view(forKey: .to) {
transitionContext.containerView.addSubview(toView)
}
} else {
if isNavigationAnimation,
let fromView = transitionContext.view(forKey: .from), let toView = transitionContext.view(forKey: .to) {
transitionContext.containerView.insertSubview(toView, belowSubview: fromView)
}
}
browser.browserView.isHidden = true
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
browser.browserView.isHidden = false
browser.maskView.alpha = self.isForShow ? 1.0 : 0
browser.browserView.alpha = self.isForShow ? 1.0 : 0
}) { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
//
// JXPhotoBrowserLog.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/12.
// Copyright © 2019 JiongXing. All rights reserved.
//
import Foundation
public struct JXPhotoBrowserLog {
/// 日志重要程度等级
public enum Level: Int {
case low = 0
case middle
case high
case forbidden
}
/// 允许输出日志的最低等级。`forbidden`为禁止所有日志
public static var level: Level = .forbidden
public static func low(_ item: @autoclosure () -> Any) {
if level.rawValue <= Level.low.rawValue {
print("[JXPhotoBrowser] [low]", item())
}
}
public static func middle(_ item: @autoclosure () -> Any) {
if level.rawValue <= Level.middle.rawValue {
print("[JXPhotoBrowser] [middle]", item())
}
}
public static func high(_ item: @autoclosure () -> Any) {
if level.rawValue <= Level.high.rawValue {
print("[JXPhotoBrowser] [high]", item())
}
}
}
//
// JXPhotoBrowserNoneAnimator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/26.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
/// 使用本类以实现不出现转场动画的需求
open class JXPhotoBrowserNoneAnimator: JXPhotoBrowserFadeAnimator {
public override init() {
super.init()
showDuration = 0
dismissDuration = 0
}
}
//
// JXPhotoBrowserNumberPageIndicator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
open class JXPhotoBrowserNumberPageIndicator: UILabel, JXPhotoBrowserPageIndicator {
/// 页码与顶部的距离
open lazy var topPadding: CGFloat = {
if #available(iOS 11.0, *),
let window = UIApplication.shared.keyWindow {
return window.safeAreaInsets.top
}
return 20
}()
public convenience init() {
self.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: frame)
config()
}
required public init?(coder: NSCoder) {
super.init(coder: coder)
config()
}
private func config() {
font = UIFont.systemFont(ofSize: 17)
textAlignment = .center
textColor = UIColor.white
backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
layer.masksToBounds = true
}
public func setup(with browser: JXPhotoBrowser) {
}
private var total: Int = 0
public func reloadData(numberOfItems: Int, pageIndex: Int) {
total = numberOfItems
text = "\(pageIndex + 1) / \(total)"
sizeToFit()
frame.size.width += frame.height
layer.cornerRadius = frame.height / 2
if let view = superview {
center.x = view.bounds.width / 2
frame.origin.y = topPadding
}
isHidden = numberOfItems <= 1
}
public func didChanged(pageIndex: Int) {
text = "\(pageIndex + 1) / \(total)"
}
}
//
// JXPhotoBrowserPageIndicator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/25.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
public protocol JXPhotoBrowserPageIndicator: UIView {
func setup(with browser: JXPhotoBrowser)
func reloadData(numberOfItems: Int, pageIndex: Int)
func didChanged(pageIndex: Int)
}
//
// JXPhotoBrowserSmoothZoomAnimator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/26.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
/// 更丝滑的Zoom动画
open class JXPhotoBrowserSmoothZoomAnimator: NSObject, JXPhotoBrowserAnimatedTransitioning {
open var showDuration: TimeInterval = 0.25
open var dismissDuration: TimeInterval = 0.25
open var isNavigationAnimation = false
public typealias TransitionViewAndFrame = (transitionView: UIView, thumbnailFrame: CGRect)
public typealias TransitionViewAndFrameProvider = (_ index: Int, _ destinationView: UIView) -> TransitionViewAndFrame?
/// 获取转场缩放的视图与前置Frame
open var transitionViewAndFrameProvider: TransitionViewAndFrameProvider = { _, _ in return nil }
/// 替补的动画方案
open lazy var substituteAnimator: JXPhotoBrowserAnimatedTransitioning = JXPhotoBrowserFadeAnimator()
public init(transitionViewAndFrame: @escaping TransitionViewAndFrameProvider) {
transitionViewAndFrameProvider = transitionViewAndFrame
}
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return isForShow ? showDuration : dismissDuration
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
if isForShow {
playShowAnimation(context: transitionContext)
} else {
playDismissAnimation(context: transitionContext)
}
}
private func playShowAnimation(context: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
context.completeTransition(!context.transitionWasCancelled)
return
}
if isNavigationAnimation,
let fromView = context.view(forKey: .from),
let fromViewSnapshot = snapshot(with: fromView),
let toView = context.view(forKey: .to) {
toView.insertSubview(fromViewSnapshot, at: 0)
}
context.containerView.addSubview(browser.view)
guard let (transitionView, thumbnailFrame, destinationFrame) = transitionViewAndFrames(with: browser) else {
// 转为执行替补动画
substituteAnimator.isForShow = isForShow
substituteAnimator.photoBrowser = photoBrowser
substituteAnimator.isNavigationAnimation = isNavigationAnimation
substituteAnimator.animateTransition(using: context)
return
}
browser.maskView.alpha = 0
browser.browserView.isHidden = true
transitionView.frame = thumbnailFrame
context.containerView.addSubview(transitionView)
UIView.animate(withDuration: showDuration, animations: {
browser.maskView.alpha = 1.0
transitionView.frame = destinationFrame
}) { _ in
browser.browserView.isHidden = false
browser.view.insertSubview(browser.maskView, belowSubview: browser.browserView)
transitionView.removeFromSuperview()
context.completeTransition(!context.transitionWasCancelled)
}
}
private func playDismissAnimation(context: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
return
}
guard let (transitionView, thumbnailFrame, destinationFrame) = transitionViewAndFrames(with: browser) else {
// 转为执行替补动画
substituteAnimator.isForShow = isForShow
substituteAnimator.photoBrowser = photoBrowser
substituteAnimator.isNavigationAnimation = isNavigationAnimation
substituteAnimator.animateTransition(using: context)
return
}
browser.browserView.isHidden = true
transitionView.frame = destinationFrame
context.containerView.addSubview(transitionView)
UIView.animate(withDuration: showDuration, animations: {
browser.maskView.alpha = 0
transitionView.frame = thumbnailFrame
}) { _ in
if let toView = context.view(forKey: .to) {
context.containerView.addSubview(toView)
}
transitionView.removeFromSuperview()
context.completeTransition(!context.transitionWasCancelled)
}
}
private func transitionViewAndFrames(with browser: JXPhotoBrowser) -> (UIView, CGRect, CGRect)? {
let browserView = browser.browserView
let destinationView = browser.view!
guard let transitionContext = transitionViewAndFrameProvider(browser.pageIndex, destinationView) else {
return nil
}
guard let cell = browserView.visibleCells[browserView.pageIndex] as? JXPhotoBrowserZoomSupportedCell else {
return nil
}
let showContentView = cell.showContentView
let destinationFrame = showContentView.convert(showContentView.bounds, to: destinationView)
return (transitionContext.transitionView, transitionContext.thumbnailFrame, destinationFrame)
}
}
//
// JXPhotoBrowserView.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/14.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
open class JXPhotoBrowserView: UIView, UIScrollViewDelegate {
/// 弱引用PhotoBrowser
open weak var photoBrowser: JXPhotoBrowser?
/// 询问当前数据总量
open lazy var numberOfItems: () -> Int = { 0 }
/// 返回可复用的Cell类。用户可根据index返回不同的类。本闭包将在每次复用Cell时实时调用。
open lazy var cellClassAtIndex: (_ index: Int) -> JXPhotoBrowserCell.Type = { _ in
JXPhotoBrowserImageCell.self
}
/// 刷新Cell数据。本闭包将在Cell完成位置布局后调用。
open lazy var reloadCellAtIndex: (JXPhotoBrowser.ReloadCellContext) -> Void = { _ in }
/// 自然滑动引起的页码改变时回调
open lazy var didChangedPageIndex: (_ index: Int) -> Void = { _ in }
/// Cell将显示
open lazy var cellWillAppear: (JXPhotoBrowserCell, Int) -> Void = { _, _ in }
/// Cell将不显示
open lazy var cellWillDisappear: (JXPhotoBrowserCell, Int) -> Void = { _, _ in }
/// Cell已显示
open lazy var cellDidAppear: (JXPhotoBrowserCell, Int) -> Void = { _, _ in }
/// 滑动方向
open var scrollDirection: JXPhotoBrowser.ScrollDirection = .horizontal
/// 项间距
open var itemSpacing: CGFloat = 30
/// 当前页码。给本属性赋值不会触发`didChangedPageIndex`闭包。
open var pageIndex = 0 {
didSet {
if pageIndex != oldValue {
isPageIndexChanged = true
}
}
}
/// 页码是否已改变
public var isPageIndexChanged = true
/// 容器
open lazy var scrollView: UIScrollView = {
let sv = UIScrollView()
sv.backgroundColor = .clear
sv.showsVerticalScrollIndicator = false
sv.showsHorizontalScrollIndicator = false
sv.isPagingEnabled = true
sv.isScrollEnabled = true
sv.delegate = self
if #available(iOS 11.0, *) {
sv.contentInsetAdjustmentBehavior = .never
}
return sv
}()
var isRotating = false
deinit {
JXPhotoBrowserLog.low("deinit - \(self.classForCoder)")
}
public convenience init() {
self.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
open func setup() {
backgroundColor = .clear
addSubview(scrollView)
}
open override func layoutSubviews() {
super.layoutSubviews()
if scrollDirection == .horizontal {
scrollView.frame = CGRect(x: 0, y: 0, width: bounds.width + itemSpacing, height: bounds.height)
} else {
scrollView.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + itemSpacing)
}
reloadData()
}
open func resetContentSize() {
let maxIndex = CGFloat(numberOfItems())
if scrollDirection == .horizontal {
scrollView.contentSize = CGSize(width: scrollView.frame.width * maxIndex,
height: scrollView.frame.height)
} else {
scrollView.contentSize = CGSize(width: scrollView.frame.width,
height: scrollView.frame.height * maxIndex)
}
}
/// 刷新数据,同时刷新Cell布局
open func reloadData() {
// 修正pageIndex,同步数据源的变更
pageIndex = max(0, pageIndex)
pageIndex = min(pageIndex, numberOfItems())
resetContentSize()
resetCells()
layoutCells()
reloadItems()
refreshContentOffset()
}
/// 根据页码更新滑动位置
open func refreshContentOffset() {
if scrollDirection == .horizontal {
scrollView.contentOffset = CGPoint(x: CGFloat(pageIndex) * scrollView.bounds.width, y: 0)
} else {
scrollView.contentOffset = CGPoint(x: 0, y: CGFloat(pageIndex) * scrollView.bounds.height)
}
}
open func scrollViewDidScroll(_ scrollView: UIScrollView) {
// 屏幕旋转时会触发本方法。此时不可更改pageIndex
if isRotating {
isRotating = false
return
}
if scrollDirection == .horizontal && scrollView.bounds.width > 0 {
pageIndex = Int(round(scrollView.contentOffset.x / (scrollView.bounds.width)))
} else if scrollDirection == .vertical && scrollView.bounds.height > 0 {
pageIndex = Int(round(scrollView.contentOffset.y / (scrollView.bounds.height)))
}
if isPageIndexChanged {
isPageIndexChanged = false
resetCells()
layoutCells()
reloadItems()
didChangedPageIndex(pageIndex)
}
}
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if let cell = visibleCells[pageIndex] {
cellDidAppear(cell, pageIndex)
}
}
//
// MARK: - 复用Cell
//
/// 显示中的Cell
open var visibleCells = [Int: JXPhotoBrowserCell]()
/// 缓存中的Cell
open var reusableCells = [String: [JXPhotoBrowserCell]]()
/// 入队
private func enqueue(cell: JXPhotoBrowserCell) {
let name = String(describing: cell.classForCoder)
if var array = reusableCells[name] {
array.append(cell)
reusableCells[name] = array
} else {
reusableCells[name] = [cell]
}
}
/// 出队,没缓存则新建
private func dequeue(cellType: JXPhotoBrowserCell.Type, browser: JXPhotoBrowser) -> JXPhotoBrowserCell {
var cell: JXPhotoBrowserCell
let name = String(describing: cellType.classForCoder())
if var array = reusableCells[name], array.count > 0 {
JXPhotoBrowserLog.middle("命中缓存!\(name)")
cell = array.removeFirst()
reusableCells[name] = array
} else {
JXPhotoBrowserLog.middle("新建Cell! \(name)")
cell = cellType.generate(with: browser)
}
return cell
}
/// 重置所有Cell的位置。更新 visibleCells 和 reusableCells
open func resetCells() {
guard let browser = photoBrowser else {
return
}
var removeFromVisibles = [Int]()
for (index, cell) in visibleCells {
if index == pageIndex {
continue
}
cellWillDisappear(cell, index)
cell.removeFromSuperview()
enqueue(cell: cell)
removeFromVisibles.append(index)
}
removeFromVisibles.forEach { visibleCells.removeValue(forKey: $0) }
// 添加要显示的cell
let itemsTotalCount = numberOfItems()
for index in (pageIndex - 1)...(pageIndex + 1) {
if index < 0 || index > itemsTotalCount - 1 {
continue
}
if index == pageIndex && visibleCells[index] != nil {
continue
}
let clazz = cellClassAtIndex(index)
JXPhotoBrowserLog.middle("Required class name: \(String(describing: clazz))")
JXPhotoBrowserLog.middle("index:\(index) 出队!")
let cell = dequeue(cellType: clazz, browser: browser)
visibleCells[index] = cell
scrollView.addSubview(cell)
}
}
/// 刷新所有显示中的Cell位置
open func layoutCells() {
let cellWidth = bounds.width
let cellHeight = bounds.height
for (index, cell) in visibleCells {
if scrollDirection == .horizontal {
cell.frame = CGRect(x: CGFloat(index) * (cellWidth + itemSpacing), y: 0, width: cellWidth, height: cellHeight)
} else {
cell.frame = CGRect(x: 0, y: CGFloat(index) * (cellHeight + itemSpacing), width: cellWidth, height: cellHeight)
}
}
}
/// 刷新所有Cell的数据
open func reloadItems() {
visibleCells.forEach { [weak self] index, cell in
guard let `self` = self else { return }
self.reloadCellAtIndex((cell, index, self.pageIndex))
cell.setNeedsLayout()
}
if let cell = visibleCells[pageIndex] {
cellWillAppear(cell, pageIndex)
}
}
}
//
// JXPhotoBrowserZoomAnimator.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/26.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
/// Zoom动画
open class JXPhotoBrowserZoomAnimator: NSObject, JXPhotoBrowserAnimatedTransitioning {
open var showDuration: TimeInterval = 0.25
open var dismissDuration: TimeInterval = 0.25
open var isNavigationAnimation = false
public typealias PreviousViewAtIndexClosure = (_ index: Int) -> UIView?
/// 转场动画的前向视图
open var previousViewProvider: PreviousViewAtIndexClosure = { _ in return nil }
/// 替补的动画方案
open lazy var substituteAnimator: JXPhotoBrowserAnimatedTransitioning = JXPhotoBrowserFadeAnimator()
public init(previousView: @escaping PreviousViewAtIndexClosure) {
previousViewProvider = previousView
}
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return isForShow ? showDuration : dismissDuration
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
if isForShow {
playShowAnimation(context: transitionContext)
} else {
playDismissAnimation(context: transitionContext)
}
}
private func playShowAnimation(context: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
context.completeTransition(!context.transitionWasCancelled)
return
}
if isNavigationAnimation,
let fromView = context.view(forKey: .from),
let fromViewSnapshot = snapshot(with: fromView),
let toView = context.view(forKey: .to) {
toView.insertSubview(fromViewSnapshot, at: 0)
}
context.containerView.addSubview(browser.view)
guard let (snap1, snap2, thumbnailFrame, destinationFrame) = snapshotsAndFrames(browser: browser) else {
// 转为执行替补动画
substituteAnimator.isForShow = isForShow
substituteAnimator.photoBrowser = photoBrowser
substituteAnimator.isNavigationAnimation = isNavigationAnimation
substituteAnimator.animateTransition(using: context)
return
}
snap1.frame = thumbnailFrame
snap2.frame = thumbnailFrame
snap2.alpha = 0
browser.maskView.alpha = 0
browser.browserView.isHidden = true
context.containerView.addSubview(snap1)
context.containerView.addSubview(snap2)
UIView.animate(withDuration: showDuration, animations: {
browser.maskView.alpha = 1.0
snap1.frame = destinationFrame
snap1.alpha = 0
snap2.frame = destinationFrame
snap2.alpha = 1.0
}) { _ in
browser.browserView.isHidden = false
browser.view.insertSubview(browser.maskView, belowSubview: browser.browserView)
snap1.removeFromSuperview()
snap2.removeFromSuperview()
context.completeTransition(!context.transitionWasCancelled)
}
}
private func playDismissAnimation(context: UIViewControllerContextTransitioning) {
guard let browser = photoBrowser else {
return
}
guard let (snap1, snap2, thumbnailFrame, destinationFrame) = snapshotsAndFrames(browser: browser) else {
// 转为执行替补动画
substituteAnimator.isForShow = isForShow
substituteAnimator.photoBrowser = photoBrowser
substituteAnimator.isNavigationAnimation = isNavigationAnimation
substituteAnimator.animateTransition(using: context)
return
}
snap1.frame = destinationFrame
snap1.alpha = 0
snap2.frame = destinationFrame
context.containerView.addSubview(snap1)
context.containerView.addSubview(snap2)
browser.browserView.isHidden = true
UIView.animate(withDuration: showDuration, animations: {
browser.maskView.alpha = 0
snap1.frame = thumbnailFrame
snap1.alpha = 0
snap2.frame = thumbnailFrame
snap2.alpha = 1.0
}) { _ in
if let toView = context.view(forKey: .to) {
context.containerView.addSubview(toView)
}
snap1.removeFromSuperview()
snap2.removeFromSuperview()
context.completeTransition(!context.transitionWasCancelled)
}
}
private func snapshotsAndFrames(browser: JXPhotoBrowser) -> (UIView, UIView, CGRect, CGRect)? {
let browserView = browser.browserView
let view = browser.view
let closure = previousViewProvider
guard let previousView = closure(browserView.pageIndex) else {
return nil
}
guard let cell = browserView.visibleCells[browserView.pageIndex] as? JXPhotoBrowserZoomSupportedCell else {
return nil
}
let thumbnailFrame = previousView.convert(previousView.bounds, to: view)
let showContentView = cell.showContentView
// 两Rect求交集,得出显示中的区域
let destinationFrame = cell.convert(cell.bounds.intersection(showContentView.frame), to: view)
guard let snap1 = fastSnapshot(with: previousView) else {
JXPhotoBrowserLog.high("取不到前截图!")
return nil
}
guard let snap2 = snapshot(with: cell.showContentView) else {
JXPhotoBrowserLog.high("取不到后截图!")
return nil
}
return (snap1, snap2, thumbnailFrame, destinationFrame)
}
}
//
// JXPhotoBrowserZoomSupportedCell.swift
// JXPhotoBrowser
//
// Created by JiongXing on 2019/11/22.
// Copyright © 2019 JiongXing. All rights reserved.
//
import UIKit
/// 在Zoom转场时使用
protocol JXPhotoBrowserZoomSupportedCell: UIView {
/// 内容视图
var showContentView: UIView { get }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment