Files
suixinkan_uikit/suixinkan/UI/Common/MediaPreviewItem.swift
T

110 lines
3.3 KiB
Swift

//
// MediaPreviewItem.swift
// suixinkan
//
import UIKit
/// 媒体预览资源项,描述可全屏预览的图片或视频。
public struct MediaPreviewItem: Hashable {
/// 媒体类型。
public enum MediaType: Equatable {
case image
case video
}
/// 预览资源来源。
public enum Source {
case remoteImage(URL)
case localImageFile(URL)
case image(UIImage)
case remoteVideo(URL)
case localVideoFile(URL)
}
/// 资源唯一标识,用于 Diffable Data Source。
public let id: UUID
/// 预览资源来源。
public let source: Source
/// 创建媒体预览资源项。
public init(id: UUID = UUID(), source: Source) {
self.id = id
self.source = source
}
/// 媒体类型。
public var mediaType: MediaType {
switch source {
case .remoteImage, .localImageFile, .image:
return .image
case .remoteVideo, .localVideoFile:
return .video
}
}
/// 是否为图片资源。
public var isImage: Bool {
mediaType == .image
}
/// 是否为视频资源。
public var isVideo: Bool {
mediaType == .video
}
/// 通过远程图片地址创建资源项,空字符串或非法地址返回 nil。
public static func remoteImage(_ urlString: String) -> MediaPreviewItem? {
makeRemoteURL(from: urlString).map { MediaPreviewItem(source: .remoteImage($0)) }
}
/// 通过远程视频地址创建资源项,空字符串或非法地址返回 nil。
public static func remoteVideo(_ urlString: String) -> MediaPreviewItem? {
makeRemoteURL(from: urlString).map { MediaPreviewItem(source: .remoteVideo($0)) }
}
/// 通过本地图片文件地址创建资源项。
public static func localImage(url: URL) -> MediaPreviewItem {
MediaPreviewItem(source: .localImageFile(url))
}
/// 通过本地图片对象创建资源项。
public static func image(_ image: UIImage) -> MediaPreviewItem {
MediaPreviewItem(source: .image(image))
}
/// 通过本地视频文件地址创建资源项。
public static func localVideo(url: URL) -> MediaPreviewItem {
MediaPreviewItem(source: .localVideoFile(url))
}
/// 将起始索引限制在可用范围内。
public static func clampedStartIndex(_ startIndex: Int, itemCount: Int) -> Int {
guard itemCount > 0 else { return 0 }
return min(max(0, startIndex), itemCount - 1)
}
/// 从媒体数组中读取安全的起始资源项。
public static func currentItem(in items: [MediaPreviewItem], startIndex: Int) -> MediaPreviewItem? {
guard !items.isEmpty else { return nil }
return items[clampedStartIndex(startIndex, itemCount: items.count)]
}
public static func == (lhs: MediaPreviewItem, rhs: MediaPreviewItem) -> Bool {
lhs.id == rhs.id
}
public func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
private static func makeRemoteURL(from text: String) -> URL? {
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty, let url = URL(string: trimmed) else { return nil }
guard let scheme = url.scheme?.lowercased(), ["http", "https"].contains(scheme) else { return nil }
return url
}
}