jumppage功能

This commit is contained in:
ddisfriend
2025-04-10 10:04:06 +08:00
parent a497e9981e
commit 1b0676b1e6
72 changed files with 12370 additions and 8077 deletions
@@ -0,0 +1,131 @@
//
// BlurLayer.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/14.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
import UIKit
private extension CGRect {
func rectangle(_ s: CGSize) -> CGRect {
let x = origin.x / s.width
let y = origin.y / s.height
let width = size.width / s.width
let height = size.height / s.height
return CGRect(x: x, y: y, width: width, height: height)
}
}
class BlurLayer: CALayer {
private static let blurRadiusKey = "blurRadius"
private static let blurLayoutKey = "blurLayout"
@NSManaged var blurRadius: CGFloat
@NSManaged private var blurLayout: CGFloat
private var fromBlurRadius: CGFloat?
var presentationRadius: CGFloat {
if let radius = fromBlurRadius {
if let layer = presentation() {
return layer.blurRadius
} else {
return radius
}
} else {
return blurRadius
}
}
override class func needsDisplay(forKey key: String) -> Bool {
if key == blurRadiusKey || key == blurLayoutKey {
return true
}
return super.needsDisplay(forKey: key)
}
open override func action(forKey event: String) -> CAAction? {
if event == BlurLayer.blurRadiusKey {
fromBlurRadius = nil
if let action = super.action(forKey: "opacity") as? CABasicAnimation {
fromBlurRadius = (presentation() ?? self).blurRadius
action.keyPath = event
action.fromValue = fromBlurRadius
return action
}
}
if event == BlurLayer.blurLayoutKey, let action = super.action(forKey: "opacity") as? CABasicAnimation {
action.keyPath = event
action.fromValue = 0
action.toValue = 1
return action
}
return super.action(forKey: event)
}
}
extension BlurLayer {
func draw(_ image: UIImage, fixes isFixes: Bool, baseLayer: CALayer?) {
contents = image.cgImage
contentsScale = image.scale
if isFixes, let blurLayer = presentation() {
contentsRect = blurLayer.convert(blurLayer.bounds, to: baseLayer).rectangle(image.size)
}
}
func refresh() {
fromBlurRadius = nil
}
func animate() {
UIView.performWithoutAnimation {
blurLayout = 0
}
blurLayout = 1
}
func render(in context: CGContext, for layer: CALayer) {
let layers = hideOverlappingLayers(layer.sublayers)
layer.render(in: context)
layers.forEach {
$0.isHidden = false
}
}
private func hideOverlappingLayers(_ layers: [CALayer]?) -> [CALayer] {
var hiddenLayers: [CALayer] = []
guard let layers = layers else {
return hiddenLayers
}
for layer in layers.reversed() {
if isHang(to: layer) {
return hiddenLayers + hideOverlappingLayers(layer.sublayers)
}
if layer.isHidden == false {
layer.isHidden = true
hiddenLayers.append(layer)
}
if layer == self {
return hiddenLayers
}
}
return hiddenLayers
}
private func isHang(to target: CALayer) -> Bool {
var layer = superlayer
while layer != nil {
if layer == target {
return true
}
layer = layer?.superlayer
}
return false
}
}
@@ -0,0 +1,33 @@
//
// CGContext+CGImage.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/17.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
import UIKit
extension CGContext {
static func imageContext(with quality: CaptureQuality, rect: CGRect, opaque: Bool) -> CGContext? {
UIGraphicsBeginImageContextWithOptions(rect.size, opaque, quality.imageScale)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.translateBy(x: -rect.origin.x, y: -rect.origin.y)
context.interpolationQuality = quality.interpolationQuality
return context
}
func makeImage(with blendColor: UIColor?, blendMode: CGBlendMode, size: CGSize) -> CGImage? {
if let color = blendColor {
setFillColor(color.cgColor)
setBlendMode(blendMode)
fill(CGRect(origin: .zero, size: size))
}
return makeImage()
}
}
@@ -0,0 +1,66 @@
//
// CGImage+Accelerate.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/17.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
import Accelerate
import UIKit
extension CGImage {
var area: Int {
return width * height
}
private var size: CGSize {
return CGSize(width: width, height: height)
}
private var bytes: Int {
return bytesPerRow * height
}
private func imageBuffer(from data: UnsafeMutableRawPointer!) -> vImage_Buffer {
return vImage_Buffer(data: data, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow)
}
func blurred(with boxSize: UInt32, iterations: Int, blendColor: UIColor?, blendMode: CGBlendMode) -> CGImage? {
guard let providerData = dataProvider?.data else {
return nil
}
let inData = malloc(bytes)
var inBuffer = imageBuffer(from: inData)
let outData = malloc(bytes)
var outBuffer = imageBuffer(from: outData)
let tempSize = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, boxSize, boxSize, nil, vImage_Flags(kvImageEdgeExtend + kvImageGetTempBufferSize))
let tempData = malloc(tempSize)
defer {
free(inData)
free(outData)
free(tempData)
}
let source = CFDataGetBytePtr(providerData)
memcpy(inBuffer.data, source, bytes)
for _ in 0..<iterations {
vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, tempData, 0, 0, boxSize, boxSize, nil, vImage_Flags(kvImageEdgeExtend))
let temp = inBuffer.data
inBuffer.data = outBuffer.data
outBuffer.data = temp
}
let context = colorSpace.flatMap {
CGContext(data: inBuffer.data, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: $0, bitmapInfo: bitmapInfo.rawValue)
}
return context?.makeImage(with: blendColor, blendMode: blendMode, size: size)
}
}
@@ -0,0 +1,34 @@
//
// CaptureQuality.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/17.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
import UIKit
public enum CaptureQuality {
case `default`
case low
case medium
case high
var imageScale: CGFloat {
switch self {
case .default, .high:
return 0
case .low, .medium:
return 1
}
}
var interpolationQuality: CGInterpolationQuality {
switch self {
case .default, .low:
return .none
case .medium, .high:
return .default
}
}
}
@@ -0,0 +1,19 @@
//
// DynamicBlurView.h
// DynamicBlurView
//
// Created by Kyohei Ito on 2015/04/08.
// Copyright (c) 2015年 kyohei_ito. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for DynamicBlurView.
FOUNDATION_EXPORT double DynamicBlurViewVersionNumber;
//! Project version string for DynamicBlurView.
FOUNDATION_EXPORT const unsigned char DynamicBlurViewVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <DynamicBlurView/PublicHeader.h>
@@ -0,0 +1,195 @@
//
// DynamicBlurView.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2015/04/08.
// Copyright (c) 2015 kyohei_ito. All rights reserved.
//
import UIKit
open class DynamicBlurView: UIView {
open override class var layerClass : AnyClass {
return BlurLayer.self
}
private var staticImage: UIImage?
private var displayLink: CADisplayLink?
private var blurLayer: BlurLayer {
return layer as! BlurLayer
}
private let mainQueue = DispatchQueue.main
private let globalQueue: DispatchQueue = {
if #available (iOS 8.0, *) {
return .global(qos: .userInteractive)
} else {
return .global(priority: .high)
}
}()
private var renderingTarget: UIView? {
if isDeepRendering {
return window
} else {
return superview
}
}
/// When true, it captures displays image and blur it asynchronously. Try to set true if needs more performance.
/// Asynchronous drawing is possibly crash when needs to process on main thread that drawing with animation for example.
open var drawsAsynchronously: Bool = false
/// Radius of blur.
open var blurRadius: CGFloat {
set { blurLayer.blurRadius = newValue }
get { return blurLayer.blurRadius }
}
/// Default is none.
open var trackingMode: TrackingMode = .none {
didSet {
if trackingMode != oldValue {
linkForDisplay()
}
}
}
/// Blend color.
open var blendColor: UIColor?
/// Blend mode.
open var blendMode: CGBlendMode = .plusLighter
/// Default is 3.
open var iterations: Int = 3
/// If the view want to render beyond the layer, should be true.
open var isDeepRendering: Bool = false
/// When none of tracking mode, it can change the radius of blur with the ratio. Should set from 0 to 1.
open var blurRatio: CGFloat = 1 {
didSet {
if let image = staticImage, oldValue != blurRatio {
draw(image, blurRadius: blurRadius, fixes: false, baseLayer: renderingTarget?.layer)
}
}
}
/// Quality of captured image.
open var quality: CaptureQuality = .medium
public override init(frame: CGRect) {
super.init(frame: frame)
isUserInteractionEnabled = false
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
isUserInteractionEnabled = false
}
open override func didMoveToWindow() {
super.didMoveToWindow()
if let view = renderingTarget, window != nil && trackingMode == .none {
staticImage = snapshotImage(for: view.layer, conversion: !isDeepRendering)
}
}
open override func didMoveToSuperview() {
super.didMoveToSuperview()
if superview == nil {
displayLink?.invalidate()
displayLink = nil
} else {
linkForDisplay()
}
}
private func async(on queue: DispatchQueue, actions: @escaping () -> Void) {
if drawsAsynchronously {
queue.async(execute: actions)
} else {
actions()
}
}
private func sync(on queue: DispatchQueue, actions: () -> Void) {
if drawsAsynchronously {
queue.sync(execute: actions)
} else {
actions()
}
}
private func draw(_ image: UIImage, blurRadius radius: CGFloat, fixes isFixes: Bool, baseLayer: CALayer?) {
async(on: globalQueue) { [weak self] in
if let me = self, let blurredImage = image.blurred(radius: radius, iterations: me.iterations, ratio: me.blurRatio, blendColor: me.blendColor, blendMode: me.blendMode) {
me.sync(on: me.mainQueue) {
me.blurLayer.draw(blurredImage, fixes: isFixes, baseLayer: baseLayer)
}
}
}
}
private func blurLayerRect(to layer: CALayer, conversion: Bool) -> CGRect {
if conversion {
let presentationLayer = blurLayer.presentation() ?? blurLayer
return presentationLayer.convert(presentationLayer.bounds, to: layer)
} else {
return layer.bounds
}
}
private func snapshotImage(for layer: CALayer, conversion: Bool) -> UIImage? {
let rect = blurLayerRect(to: layer, conversion: conversion)
guard let context = CGContext.imageContext(with: quality, rect: rect, opaque: isOpaque) else {
return nil
}
blurLayer.render(in: context, for: layer)
defer {
UIGraphicsEndImageContext()
}
return UIGraphicsGetImageFromCurrentImageContext()
}
}
extension DynamicBlurView {
open override func display(_ layer: CALayer) {
let blurRadius = blurLayer.presentationRadius
let isFixes = isDeepRendering && staticImage != nil
if let view = renderingTarget, let image = staticImage ?? snapshotImage(for: view.layer, conversion: !isFixes) {
draw(image, blurRadius: blurRadius, fixes: isFixes, baseLayer: view.layer)
}
}
}
extension DynamicBlurView {
private func linkForDisplay() {
displayLink?.invalidate()
displayLink = UIScreen.main.displayLink(withTarget: self, selector: #selector(DynamicBlurView.displayDidRefresh(_:)))
displayLink?.add(to: .main, forMode: RunLoop.Mode(rawValue: trackingMode.description))
}
@objc private func displayDidRefresh(_ displayLink: CADisplayLink) {
display(layer)
}
}
extension DynamicBlurView {
/// Remove cache of blur image then get it again.
open func refresh() {
blurLayer.refresh()
staticImage = nil
blurRatio = 1
display(layer)
}
/// Remove cache of blur image.
open func remove() {
blurLayer.refresh()
staticImage = nil
blurRatio = 1
layer.contents = nil
}
/// Should use when needs to change layout with animation when is set none of tracking mode.
public func animate() {
blurLayer.animate()
}
}
@@ -0,0 +1,27 @@
//
// TrackingMode.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/17.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
import UIKit
public enum TrackingMode: CustomStringConvertible {
case tracking
case common
case none
public var description: String {
switch self {
case .tracking:
return RunLoop.Mode.tracking.rawValue
case .common:
return RunLoop.Mode.common.rawValue
case .none:
return ""
}
}
}
@@ -0,0 +1,30 @@
//
// UIImage+Blur.swift
// DynamicBlurView
//
// Created by Kyohei Ito on 2017/08/11.
// Copyright © 2017 kyohei_ito. All rights reserved.
//
import UIKit
public extension UIImage {
func blurred(radius: CGFloat, iterations: Int, ratio: CGFloat, blendColor color: UIColor?, blendMode mode: CGBlendMode) -> UIImage? {
guard let cgImage = cgImage else {
return nil
}
if cgImage.area <= 0 || radius <= 0 {
return self
}
var boxSize = UInt32(radius * scale * ratio)
if boxSize % 2 == 0 {
boxSize += 1
}
return cgImage.blurred(with: boxSize, iterations: iterations, blendColor: color, blendMode: mode).map {
UIImage(cgImage: $0, scale: scale, orientation: imageOrientation)
}
}
}