jsdw_ios/QuickLocation/Plugin/ImagePlugin.swift

298 lines
9.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// ImagePlugin.swift
// DLSDK
//
// Created by osell on 2023/4/14.
//
import UIKit
import Kingfisher
import CoreMedia
nonisolated(unsafe) let kBrokenNetworkImage = UIImage(named: "Common/image_broken") ?? UIImage()
extension UIImage {
static var brokenNetworkImage: UIImage { kBrokenNetworkImage }
}
private let kNetworkImageFailureOptions: KingfisherOptionsInfo = [
.onFailureImage(kBrokenNetworkImage)
]
public protocol ImageSource {
/// 图片资源URL
var imageURL: URL? { get }
/// 图片资源字符串
var imageStr: String? { get }
}
extension ImageSource {
var imageURL: URL? { nil }
var imageStr: String? { nil }
}
extension String: ImageSource {
public var imageURL: URL? { URL(string: self.urlEncodingWithQueryAllowedCharacters) }
public var imageStr: String? { self }
}
extension URL: ImageSource {
public var imageURL: URL? { self }
public var imageStr: String? { self.absoluteString.urlDecoded }
}
// MARK: - UIImageView
extension DLWrapper where Base: UIImageView {
/// 设置图片
/// - Parameters:
/// - url: 图片链接/名称, URL or String
/// - placeholder: 可选,默认nil
/// - completionHandler: 可选,默认为nil
@MainActor public func setImage(with url: ImageSource?,
placeholder: UIImage? = nil,
completionHandler: ((UIImage?, Error?) -> Void)? = nil) {
guard let imageURL = url?.imageURL else {
let imageStr = url?.imageStr ?? ""
// 如果是本地图片,加载本地图片
if let image = UIImage(named: imageStr) {
base.image = image
return
}
// 如果是SDK中图片,则加载SDK中图片
if let image = UIImage(named: imageStr) {
base.image = image
return
}
base.image = placeholder ?? .brokenNetworkImage
return
}
// 如果是视频, 获取视频第一帧
if imageURL.imageStr?.contains(".mp4") == true {
let source = AVAssetImageDataProvider(assetURL: imageURL, time: CMTimeMakeWithSeconds(0.0, preferredTimescale: 1))
base.kf.setImage(
with: source.convertToSource(),
placeholder: placeholder,
options: kNetworkImageFailureOptions
) { result in
switch result {
case let .success(imageinfo):
completionHandler?(imageinfo.image, nil)
case let .failure(error):
completionHandler?(nil, error)
}
}
} else {
base.kf.setImage(
with: imageURL,
placeholder: placeholder,
options: kNetworkImageFailureOptions
) { result in
switch result {
case let .success(imageinfo):
completionHandler?(imageinfo.image, nil)
case let .failure(error):
completionHandler?(nil, error)
}
}
}
}
}
// MARK: - Button
extension DLWrapper where Base: UIButton {
/// 设置按钮图片
/// - Parameters:
/// - url: 图片链接, URL or String
/// - placeholder: 可选,默认nil
/// - completionHandler: 可选,默认为nil
@MainActor public func setImage(with url: ImageSource?,
for state: UIControl.State,
placeholder: UIImage? = nil,
completionHandler: ((UIImage?, Error?) -> Void)? = nil) {
guard let imageURL = url?.imageURL else {
let imageStr = url?.imageStr ?? ""
// 如果是本地图片,加载本地图片
if let image = UIImage(named: imageStr) {
base.setImage(image, for: state)
return
}
// 如果是SDK中图片,则加载SDK中图片
if let image = UIImage(named: imageStr) {
base.setImage(image, for: state)
return
}
// 都不存在,则置为nil
base.setImage(placeholder ?? .brokenNetworkImage, for: state)
return
}
base.kf.setImage(
with: imageURL,
for: state,
placeholder: placeholder,
options: kNetworkImageFailureOptions,
completionHandler: { result in
switch result {
case let .success(imageinfo):
completionHandler?(imageinfo.image, nil)
case let .failure(error):
completionHandler?(nil, error)
}
}
)
}
/// 设置按钮背景图片
/// - Parameters:
/// - url: 图片链接, URL or String
/// - placeholder: 可选,默认nil
/// - completionHandler: 可选,默认为nil
@MainActor public func setBackgroundImage(with url: ImageSource?,
for state: UIControl.State,
placeholder: UIImage? = nil,
completionHandler: ((UIImage?, Error?) -> Void)? = nil) {
guard let imageURL = url?.imageURL else {
let imageStr = url?.imageStr ?? ""
// 如果是本地图片,加载本地图片
if let image = UIImage(named: imageStr) {
base.setBackgroundImage(image, for: state)
return
}
// 如果是SDK中图片,则加载SDK中图片
if let image = UIImage(named: imageStr) {
base.setBackgroundImage(image, for: state)
return
}
// 都不存在,则置为nil
base.setBackgroundImage(placeholder ?? .brokenNetworkImage, for: state)
return
}
base.kf.setBackgroundImage(
with: imageURL,
for: state,
placeholder: placeholder,
options: kNetworkImageFailureOptions,
completionHandler: { result in
switch result {
case let .success(imageinfo):
completionHandler?(imageinfo.image, nil)
case let .failure(error):
completionHandler?(nil, error)
}
}
)
}
}
enum HeadPic {
static let placeholder = UIImage(named: "UserIcon/1") ?? UIImage()
static func isRemote(_ value: String) -> Bool {
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
return trimmed.hasPrefix("http://") || trimmed.hasPrefix("https://")
}
static func resolved(_ values: String?...) -> String {
let trimmed = values
.compactMap { $0?.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
if let remote = trimmed.first(where: { isRemote($0) }) {
return remote
}
return trimmed.first ?? ""
}
static func localImage(_ value: String) -> UIImage {
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return placeholder }
return UIImage(named: "UserIcon/\(trimmed)") ?? placeholder
}
static func remoteURL(for value: String) -> URL? {
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
guard isRemote(trimmed) else { return nil }
return trimmed.imageURL ?? URL(string: trimmed)
}
static func image(for value: String) -> UIImage {
guard let url = remoteURL(for: value) else {
return localImage(value)
}
let key = url.cacheKey
if let cached = ImageCache.default.retrieveImageInMemoryCache(forKey: key) {
return cached
}
return UIImage()
}
static func load(_ value: String, completion: @escaping (UIImage) -> Void) {
guard let url = remoteURL(for: value) else {
completion(localImage(value))
return
}
KingfisherManager.shared.retrieveImage(with: url) { result in
let image: UIImage
switch result {
case let .success(value):
image = value.image
case .failure:
image = .brokenNetworkImage
}
DispatchQueue.main.async {
completion(image)
}
}
}
}
private enum HeadPicAssociatedKeys {
static var lastValue: UInt8 = 0
}
extension UIImageView {
private var lastHeadPicValue: String {
get { objc_getAssociatedObject(self, &HeadPicAssociatedKeys.lastValue) as? String ?? "" }
set { objc_setAssociatedObject(self, &HeadPicAssociatedKeys.lastValue, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) }
}
func setHeadPic(_ value: String?) {
let trimmed = (value ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else {
lastHeadPicValue = ""
kf.cancelDownloadTask()
image = HeadPic.placeholder
return
}
let cached = HeadPic.image(for: trimmed)
let hasCachedImage = cached.size.width > 0
if lastHeadPicValue == trimmed, hasCachedImage, image === cached {
return
}
lastHeadPicValue = trimmed
if let url = HeadPic.remoteURL(for: trimmed) {
if !hasCachedImage {
kf.cancelDownloadTask()
image = nil
}
kf.setImage(
with: url,
placeholder: hasCachedImage ? cached : nil,
options: kNetworkImageFailureOptions
)
} else {
kf.cancelDownloadTask()
image = cached
}
}
}