前言

我正在参加技能社区创作者签约方案招募活动,点击链接报名投稿。

iOS下相似的图片阅读器不管是OC版别仍是Swift版别现在已经开源了不少。可是作为一个六七年的老iOS开发者,以及自己从前积累了的不少社交App经验,仍是忍不住基于自己的主意以及现在项目中相似的组件从头撸了一个。毫无疑问,此次开源的JFHeroBrowser,首选语言是Swift(完全Swift不包括任何OC代码),倾向更Swifty的方法-面向协议处理数据模型,还有Swift进阶枚举用法,命名空间等,假如你想深入学习Swift,我相信本组件会让你有不同的体验,别的因为楼主一起也在开发Flutter,编码方法上也是更”呼应式”。并且与大多数三方库内置ImageCache(大多是SDWebImage)不同,本组件,不包括内置的ImageCache,可是假如您集成了本项目作为图片阅读,网络图这块,您需求自行完成HeroNetworkImageProvider协议,能够运用Kingfisher或SDWebImage亦或是你项目中自行设计的图片缓存,完美处理组件耦合问题,具体运用参阅下面用法。别的本组件支撑多种资源格式,如本地图(UIImage),网络图(url),data(二进制),视频(url),甚至你自行完成ImageVM也能够接入你想要的资源。话不多说,我们来看具体运用方法。

下载安装地址

cocoaPods:


pod 'JFHeroBrowser', '1.3.2'

github:

github.com/JerryFans/J…

Usage

首先初始化配置

如上面所说,在Appdelegate didFinish处自行接入HeroNetworkImageProvider。完成func downloadImage(with imgUrl: String, complete: Complete?)


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
JFHeroBrowserGlobalConfig.default.networkImageProvider = HeroNetworkImageProvider.shared
// JFHeroBrowserGlobalConfig.default.networkImageProvider = SDWebImageNetworkImageProvider.shared
return true
}

Kingfisher参阅


extension HeroNetworkImageProvider: NetworkImageProvider {
func downloadImage(with imgUrl: String, complete: Complete<UIImage>?) {
KingfisherManager.shared.retrieveImage(with: URL(string: imgUrl)!, options: nil) { receiveSize, totalSize in
guard totalSize > 0 else { return }
let progress:CGFloat = CGFloat(CGFloat(receiveSize) / CGFloat(totalSize))
complete?(.progress(progress))
} downloadTaskUpdated: { task in
} completionHandler: { result in
switch result {
case .success(let loadingImageResult):
complete?(.success(loadingImageResult.image))
break
case .failure(let error):
complete?(.failed(error))
break
}
}
}
}
class HeroNetworkImageProvider: NSObject {
@objc static let shared = HeroNetworkImageProvider()
}

SDWebImage参阅


extension SDWebImageNetworkImageProvider: NetworkImageProvider {
func downloadImage(with imgUrl: String, complete: Complete<UIImage>?) {
SDWebImageManager.shared.loadImage(with: URL(string: imgUrl)) { receiveSize, totalSize, url in
guard totalSize > 0 else { return }
let progress:CGFloat = CGFloat(CGFloat(receiveSize) / CGFloat(totalSize))
complete?(.progress(progress))
} completed: { image, data, error, _, isfinished, url in
if let error = error {
complete?(.failed(error))
} else if let image = image {
complete?(.success(image))
} else {
complete?(.failed(nil))
}
}
}
}
class SDWebImageNetworkImageProvider: NSObject {
@objc static let shared = SDWebImageNetworkImageProvider()
}

然后界说你需求阅读的ViewModule

现在支撑HeroBrowserNetworkImageViewModule、HeroBrowserDataImageViewModule、HeroBrowserLocalImageViewModule、HeroBrowserVideoViewModule四种类型ViewModule。理论上还能够界说AssetImageViewModule(支撑从相册阅读图片),在我别的一个未开源的相册组件里面运用了,所以ViewModule的扩展十分方便运用者去扩展各种各样的场景,并且单一场景,因为某些特定场景比较不场景,我这只供给几种常用的场景。

几种ViewModule代码示例


//视频
let vm1 = HeroBrowserVideoViewModule(thumbailImgUrl: "https://www.6hu.cc/wp-content/uploads/2023/06/1685836468-9fcb1ae5ab1dad4.jpg", fileUrlPath: path, provider: HeroNetworkImageProvider.shared, autoPlay: false)
list.append(vm1)
//本地图(UIImage)
list.append(HeroBrowserLocalImageViewModule(image: img))
//data图 (file Image支撑转二进制,或者flutter的Uin8List)
list.append(HeroBrowserDataImageViewModule(data: imageSource[i]))
//网络图 也是最常用场景
list.append(HeroBrowserNetworkImageViewModule(thumbailImgUrl: thumbs[i], originImgUrl: origins[i]))

然后是具体运用示例

阅读图片

self是当时控制器,写了一个hero的命名空间,viewModules便是上面一个个界说的viewModule示例,支撑视频或者不同图片VM混搭也是能够的。

别的支撑参数:

  • pageControlType (默许pageControl或者数字1/5相似)

  • heroView (也便是你要放大缩放那个imageView,假如不填就不会有缩放的作用,便是一个alpha突变)

  • heroBrowserDidLongPressHandle (长按回调,能够做些保存图片、共享等动作)

  • imageDidChangeHandle (切换图片后,上个页面的imageView也要切换,才能够dissmiss回到相应方位,假如不设置便是alpha作用)

  • enableBlurEffect 是否开启毛玻璃布景,默许开启,false便是黑色布景。


self.hero.browserPhoto(viewModules: list, initIndex: indexPath.item) {
[
.pageControlType(.pageControl),
.heroView(cell.imageView),
.heroBrowserDidLongPressHandle({ [weak self] heroBrowser,vm in
self?.longPressHandle(vm: vm)
}),
.imageDidChangeHandle({ [weak self] imageIndex in
guard let self = self else { return nil }
guard let cell = self.collectionView.cellForItem(at: IndexPath(item: imageIndex, section: 0)) as? NetworkImageCollectionViewCell else { return nil }
let rect = cell.convert(cell.imageView.frame, to: self.view)
if self.view.frame.contains(rect) {
return cell.imageView
}
return nil
})
]
}
阅读单个视频

作用:

支持SwiftUI!Swift版图片&视频浏览器-JFHeroBrowser上线啦


let vm = HeroBrowserVideoViewModule(thumbailImgUrl: "https://www.6hu.cc/wp-content/uploads/2023/06/1685836474-dfbc2f50e8a22b2.jpg", videoUrl: "http://image.jerryfans.com/w_720_h_1280_d_41_2508b8aa06a2e30d2857f9bcbdfd1de0_iOS.mp4", provider: HeroNetworkImageProvider.shared, autoPlay: true)
self.hero.browserVideo(viewModule: vm)
阅读混合资源(图片+视频,或多个视频)

lazy var list: [HeroBrowserViewModuleBaseProtocol] = {
var list: [HeroBrowserViewModuleBaseProtocol] = []
let vm = HeroBrowserVideoViewModule(thumbailImgUrl: "https://www.6hu.cc/wp-content/uploads/2023/06/1685836474-dfbc2f50e8a22b2.jpg", videoUrl: "http://image.jerryfans.com/w_720_h_1280_d_41_2508b8aa06a2e30d2857f9bcbdfd1de0_iOS.mp4", provider: HeroNetworkImageProvider.shared, autoPlay: true)
list.append(vm)
list.append(HeroBrowserLocalImageViewModule(image: UIImage(named: "template-1")!))
if let path = Bundle.main.path(forResource: "bf.MOV", ofType: nil) {
let vm1 = HeroBrowserVideoViewModule(thumbailImgUrl: "https://www.6hu.cc/wp-content/uploads/2023/06/1685836468-9fcb1ae5ab1dad4.jpg", fileUrlPath: path, provider: HeroNetworkImageProvider.shared, autoPlay: false)
list.append(vm1)
}
return list
}()
self.hero.browserMultiSoures(viewModules: self.list, initIndex: 1) {
[
.enableBlurEffect(false),
.heroView(button.imageView),
.imageDidChangeHandle({ [weak self] imageIndex in
guard let self = self else { return nil }
guard let btn = self.view.viewWithTag(imageIndex) as? UIButton else { return nil }
return btn.imageView
})
]
}

SwiftUI的支撑

一开始的主意是经过官方的UIViewController转换成SwiftUI的写法,但完成中发现不少问题,特别是转场作用无从下手。假如要纯SwiftUI代码完成,看来只能运用SwiftUI布局的方法重写,等待之后有空能够做个测验。可是实际上,HeroBrowser是经过modal的方法出场的,我们直接获取rootViewController直接跳转亦可,可是便是缺少缩放动画,运用了默许的alpha转场,代码如下。本demo也提交到github了,有需求能够查阅。

支持SwiftUI!Swift版图片&视频浏览器-JFHeroBrowser上线啦

配置初始化代码


class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
JFHeroBrowserGlobalConfig.default.networkImageProvider = HeroNetworkImageProvider.shared
return true
}
}
@main
struct SwiftUIExampleApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

//获取顶层vc
let keyWindow = UIApplication.shared.connectedScenes
.map({ $0 as? UIWindowScene })
.compactMap({ $0 })
.first?.windows.first
let myAppRootVC : UIViewController? = keyWindow?.rootViewController
//从一个图片 GridView 跳转阅读
LazyVGrid(columns: columns) {
ForEach(1..<origins.count, id:\.self) { index in
ImageCell(index: index).frame(height: columns.count == 1 ? 300 : 150).onTapGesture {
var list: [HeroBrowserViewModule] = []
for i in 0..<origins.count {
list.append(HeroBrowserNetworkImageViewModule(thumbailImgUrl: thumbs[i], originImgUrl: origins[i]))
}
myAppRootVC?.hero.browserPhoto(viewModules: list, initIndex: index)
}
}
}

附件地址

  • 项目GitHub地址: JFHeroBrowser

  • Example github.com/JerryFans/J…

  • SwiftUIExample github.com/JerryFans/J…

结尾

我正在参加技能社区创作者签约方案招募活动,点击链接报名投稿。