当前位置: 首页 > news >正文

DownloadingImages 下载缓存图片,显示图片文字列表

1. 用到的技术点:

  1) Codable : 可编/解码 JSON 数据

  2) background threads : 后台线程

  3) weak self : 弱引用

  4) Combine : 取消器/组合操作

  5) Publishers and Subscribers : 发布者与订阅者

  6) FileManager : 文件管理器

  7) NSCache : 缓存

2. 网址:

  2.1 测试接口网址:

jsonplaceholdericon-default.png?t=N7T8https://jsonplaceholder.typicode.com/

  2.2 JSON 转 Model 网址:

quicktypeicon-default.png?t=N7T8https://app.quicktype.io/

3. 项目结构图

4. Model 层

  4.1 创建 PhotoModel.swift 文件

import Foundationstruct PhotoModel: Identifiable, Codable{let albumId: Intlet id: Intlet title: Stringlet url: Stringlet thumbnailUrl: String
}/*{"albumId": 1,"id": 1,"title": "accusamus beatae ad facilis cum similique qui sunt","url": "https://via.placeholder.com/600/92c952","thumbnailUrl": "https://via.placeholder.com/150/92c952"}*/

5. 工具类

  5.1 创建请求数据服务类,PhotoModelDataService.swift

import Foundation
import Combine/// 请求数据服务
class PhotoModelDataService{// 单例模式 Singletonstatic let instance = PhotoModelDataService()// 返回 JSON 数据,解码成模型@Published var photoModel:[PhotoModel] = []// 随时取消请求var cancellables = Set<AnyCancellable>()// 只能内部实例化,保证一个 App 只有一次实例化private init() {downloadData()}// 测试接口网址: https://jsonplaceholder.typicode.com/// 下载数据func downloadData(){// 获取 URLguard let url = URL(string: "https://jsonplaceholder.typicode.com/photos") else { return }// 进行请求URLSession.shared.dataTaskPublisher(for: url).subscribe(on: DispatchQueue.global(qos: .background)).receive(on: DispatchQueue.main).tryMap(handleOutput).decode(type: [PhotoModel].self, decoder: JSONDecoder()).sink { completion inswitch(completion){case .finished:breakcase .failure(let error):print("Error downloading data. \(error)")break}} receiveValue: { [weak self] returnedPhotoModel inguard let self  = self else { return }self.photoModel = returnedPhotoModel}// 随时取消.store(in: &cancellables)}// 输出数据private func handleOutput(output: URLSession.DataTaskPublisher.Output) throws -> Data{guardlet response = output.response as? HTTPURLResponse,response.statusCode >= 200 && response.statusCode < 300 else {throw URLError(.badServerResponse)}return output.data}
}

  5.2 创建图片缓存管理器类,PhotoModelCacheManager.swift

import Foundation
import SwiftUI/// 图片缓存管理器
class PhotoModelCacheManager{// 单例模式static let instance = PhotoModelCacheManager()// 只能内部实例化,保证一个 App 只有一次实例化private init() {}// 图片数量缓存,计算型属性var photoCache: NSCache<NSString, UIImage> = {let cache = NSCache<NSString, UIImage>()cache.countLimit = 200cache.totalCostLimit = 1024 * 1024 * 200  // 200mbreturn cache}()// 添加func add(key: String, value: UIImage){photoCache.setObject(value, forKey: key as NSString)}// 获取func get(key: String) -> UIImage? {return photoCache.object(forKey: key as NSString)}
}

  5.3 创建储存图片文件管理类,PhotoModelFileManager.swift

import Foundation
import SwiftUI// 存储图片文件管理器
class PhotoModelFileManager{// 单例模式static let instance = PhotoModelFileManager()let folderName = "downloaded_photos"private init(){createFolderIfNeeded()}// 创建存放图片的目录private func createFolderIfNeeded(){guard let url = getFolderPath() else { return }if !FileManager.default.fileExists(atPath: url.path){do {try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)print("Created folder success.")} catch let error {print("Error creating folder. \(error)")}}}// 创建文件夹路径private func getFolderPath()-> URL?{return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent(folderName)}// .../downloaded_photos// .../downloaded_photos/image_name.png/// 获取图片路径/// - Parameter key: 名字/// - Returns: 图片路径private func getImagePath(key: String) -> URL?{guard let folder = getFolderPath() else { return nil}return folder.appendingPathComponent(key + ".png")}// 添加图片func add(key: String, value: UIImage){// 获取数据和路径guard let data = value.pngData(),let url  = getImagePath(key: key) else { return }// 文件写人数据do {try data.write(to: url)print("Saving to file success.")} catch let error {print("Error saving to file manager. \(error)")}}// 获取图片func get(key: String) -> UIImage?{guardlet path = getImagePath(key: key)?.path,FileManager.default.fileExists(atPath: path) else {//print("Error getting path.")return nil}return UIImage(contentsOfFile: path)}
}

6. ViewModel 层

  6.1 创建下载图片 ViewModel 类,DownloadingImageViewModel.swift

import Foundation
import Combineclass DownloadingImageViewModel: ObservableObject{// 数组模型@Published var dataArray:[PhotoModel] = []// 请求数据服务let dataService = PhotoModelDataService.instance// 取消操作var cancellables = Set<AnyCancellable>()init() {addSubscribers()}// 订阅数据func addSubscribers(){dataService.$photoModel.sink {[weak self] returnedPhotoModel inguard let self = self else { return }self.dataArray = returnedPhotoModel}.store(in: &cancellables)}
}

  6.2 创建图片加载 ViewModel 类,ImageLoadingViewModel.swift

import Foundation
import SwiftUI
import Combineclass ImageLoadingViewModel: ObservableObject{@Published var image: UIImage?@Published var isLoading: Bool = false// 取消var cancellables = Set<AnyCancellable>()// 缓存管理器let manager = PhotoModelFileManager.instancelet urlString: Stringlet imageKey: Stringinit(url: String, key: String) {urlString = urlimageKey = keygetImage()}// 获取图片func getImage() {if let saveImage =  manager.get(key: imageKey){image = saveImageprint("Getting saved image.")}else{downLoadImage()print("Downloading image now!")}}// 下载图片func downLoadImage(){isLoading = trueguard let url = URL(string: urlString) else {isLoading = falsereturn}// 请求URLSession.shared.dataTaskPublisher(for: url).map { UIImage(data: $0.data) }.receive(on: DispatchQueue.main).sink { [weak self] _ inself?.isLoading = false} receiveValue: { [weak self] returnedImage inguardlet self = self,let image = returnedImage else { return }self.image = image// 下载的图像保存在缓存中self.manager.add(key: imageKey, value: image)}.store(in: &cancellables)}
}

7. 创建 View 层

  7.1 创建下载,缓存,显示图片视图,DownloadingImageView.swift

import SwiftUI/// 下载,缓存,显示图片
struct DownloadingImageView: View {@StateObject var loaderViewModel: ImageLoadingViewModelinit(url: String, key: String) {// _ : 加载器  wrappedValue: 包装器_loaderViewModel = StateObject(wrappedValue: ImageLoadingViewModel(url: url, key: key))}var body: some View {ZStack {if loaderViewModel.isLoading{ProgressView()}else if let image = loaderViewModel.image{Image(uiImage: image).resizable().clipShape(Circle())}}}
}struct DownloadingImageView_Previews: PreviewProvider {static var previews: some View {DownloadingImageView(url: "https://via.placeholder.com/600/92c952", key: "1").frame(width: 75, height: 75).previewLayout(.sizeThatFits)}
}

  7.2 创建下载显示图片文字行视图,DownloadingImagesRow.swift

import SwiftUIstruct DownloadingImagesRow: View {let model : PhotoModelvar body: some View {HStack {DownloadingImageView(url: model.url, key: "\(model.id)").frame(width: 75, height: 75)VStack (alignment: .leading){Text(model.title).font(.headline)Text(model.url).foregroundColor(.gray).italic()}.frame( maxWidth: .infinity, alignment: .leading)}}
}struct DownloadingImagesRow_Previews: PreviewProvider {static var previews: some View {DownloadingImagesRow(model: PhotoModel(albumId: 1, id: 1, title: "title", url: "https://via.placeholder.com/600/92c952", thumbnailUrl: "thumbnaolUrl here")).padding().previewLayout(.sizeThatFits)}
}

  7.3 创建下载显示图片文字列表视图,DownloadingImagesBootcamp.swift

import SwiftUI// Codable : 可编/解码 JSON 数据
// background threads : 后台线程
// weak self : 弱引用
// Combine : 取消器/组合操作
// Publishers and Subscribers : 发布者与订阅者
// FileManager : 文件管理器
// NSCache : 缓存struct DownloadingImagesBootcamp: View {@StateObject var viewModel = DownloadingImageViewModel()var body: some View {NavigationView {List {ForEach(viewModel.dataArray) { model inDownloadingImagesRow(model: model)}}.navigationTitle("Downloading Images")}}
}struct DownloadingImagesBootcamp_Previews: PreviewProvider {static var previews: some View {DownloadingImagesBootcamp()}
}

8. 效果图:

相关文章:

DownloadingImages 下载缓存图片,显示图片文字列表

1. 用到的技术点: 1) Codable : 可编/解码 JSON 数据 2) background threads : 后台线程 3) weak self : 弱引用 4) Combine : 取消器/组合操作 5) Publishers and Subscribers : 发布者与订阅者 6) FileManager : 文件管理器 7) NSCache : 缓存 2. 网址: 2.1 测试接口网址: …...

【应用层协议】HTTPS的加密流程

目录 一、认识HTTPS 二、密文 1、对称加密 2、非对称加密 三、HTTPS加密流程 1、建立连接 2、证书验证 3、密钥协商 4、数据传输 5、关闭连接 总结 在数字化时代&#xff0c;互联网已经成为我们生活和工作中不可或缺的一部分。然而&#xff0c;随着数据的不断增加&a…...

最新AI创作系统/AI绘画系统/ChatGPT系统+H5源码+微信公众号版+支持Prompt应用

一、AI创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的AI智能问答系统和AI绘画系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图…...

Z410 2023款无人机,专为零基础开发者打造的入门级开源无人机

为什么开发Z410升级款-Easydrone无人机 新手开发者通常在本科阶段加入人工智能行业&#xff0c;对无人机二次开发往往一知半解&#xff0c;面临着C、Python、ROS和mavlink等一系列入门知识&#xff0c;学习起来非常困难&#xff0c;学习的过程中也面临许多挫折。为了帮助零基础…...

elementui修改message消息提示颜色

/* el弹出框样式 */ .el-message {top: 80px !important;border: 0; }.el-message * {color: var(--white) !important;font-weight: 600; }.el-message--success {background: var(--themeBackground); }.el-message--warning {background: var(--gradientBG); }.el-message--…...

Linux和Hadoop的学习

目录 1. Linux的常用快捷键2. Hadoop集群部署问题汇总 1. Linux的常用快捷键 复制&#xff1a;CtrlshiftC 粘贴&#xff1a;CtrlshiftV TAB&#xff1a;补全命令 编写输入&#xff1a;i 退出编写&#xff1a;esc 保存并退出&#xff1a;shift&#xff1a; 2. Hadoop集群部署问…...

通达信指标预警信号,自动发送给微信好友1.0

1.功能介绍&#xff1a;十一节假日期间写了一个&#xff0c;可将股票指标预警信号&#xff0c;自动发送给微信好友/微信群&#xff08;即电脑端的消息&#xff0c;通过模拟微信操作可在手机上显示&#xff09;。本工具按通达信写的&#xff0c;如果大智慧&#xff0c;同花顺也能…...

浅谈CDN内容分发与全局负载均衡

CDN简介 CDN的全称是Content Delivery Network&#xff0c;即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需内容&#xff0c…...

【框架风格】解释器模式

1、描述 解释器框架风格&#xff08;Interpreter Framework Style&#xff09;是一种软件架构风格&#xff0c;其核心思想是构建一个解释器&#xff08;Interpreter&#xff09;来解释并执行特定领域或问题领域的语言或规则。以下是解释器框架风格的一些特点&#xff1a; 1. 领…...

c++视觉图像线性混合

图像线性混合 使用 cv::addWeighted() 函数对两幅图像进行线性混合。alpha 和 beta 是两幅图像的权重&#xff0c;它们之和应该等于1。gamma 是一个可选的增益&#xff0c;这里设置为0。 你可以通过调整 alpha 的值来改变混合比例。如果 alpha0.5&#xff0c;则两幅图像等权重…...

Doris 2.0.1 DockerFile版 升级实战

1、Doris 2.0.1 DockerFile 的制作 参考 Doris 2.0.1 Dockerfile制作-CSDN博客 2、之前的Doris 集群通过 Docker容器进行的部署&#xff0c;需提前准备好Doris2.0.1的镜像包 参考&#xff1a; 集群升级 - Apache Doris Doris 升级请遵守不要跨两个及以上关键节点版本升级的…...

kotlin aes 加密解密

文章目录 1. key填充2. 加密3. 解密 1. key填充 aes算法对key的字节数有要求 所以对输入的key要做填充处理 fun fillKey(key: String): ByteArray {val random SecureRandom.getInstance("SHA1PRNG")random.setSeed(key.toByteArray())val generator KeyGenerato…...

sqlite3的lib和头文件在哪下载 2023/9/19 上午10:46:43

2023/9/19 上午10:46:43 sqlite3的lib和头文件在哪下载 2023/9/19 上午10:46:54 你可以从SQLite官方网站下载SQLite的lib和头文件。请按照以下步骤进行操作: 打开SQLite官方网站:https://www.sqlite.org/index.html 在页面上方的菜单中选择 “Download”(下载)。 在下载…...

磁通量概述

磁通量指的是设在磁感应强度为B的匀强磁场中&#xff0c;有一个面积为S且与磁场方向垂直的平面&#xff0c;磁感应强度B与面积S的乘积&#xff0c;叫做穿过这个平面的磁通量&#xff0c;简称磁通&#xff08;Magnetic Flux&#xff09;。标量&#xff0c;符号“Φ”。在一般情况…...

MySql 终端常用指令

一、开发背景 利用数据库实现数据的增删改查 二、开发环境 Window10 mysql-8.0.33-win64 三、实现步骤 1、管理员模式打开终端 2、登录数据库&#xff08;停止 开启 登录&#xff09; 具体指令参考 MySql 安装篇 ​​​​​​​ ​​…...

【React-hooks篇幅】自定义hooks

首先得了解自定义 Hooks 跟普通函数区别在于哪里&#xff1f; Hooks 只应该在 React 函数组件内调用&#xff0c;而不应该在普通函数调用。Hooks 能够调用诸如 useState、useEffect、useContext等&#xff0c;普通函数则不能。由此可以通过内置的Hooks等来获得Firber的访问方式…...

面试算法21:删除倒数第k个节点

题目 如果给定一个链表&#xff0c;请问如何删除链表中的倒数第k个节点&#xff1f;假设链表中节点的总数为n&#xff0c;那么1≤k≤n。要求只能遍历链表一次。 例如&#xff0c;输入图4.1&#xff08;a&#xff09;中的链表&#xff0c;删除倒数第2个节点之后的链表如图4.1&a…...

数据结构——排序算法(C语言)

本篇将详细讲一下以下排序算法&#xff1a; 直接插入排序希尔排序选择排序快速排序归并排序计数排序 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某写关键字的大小&#xff0c;按照递增或递减0排列起来的操作。 稳定性的概念…...

基于Http Basic Authentication的接口

Basic Authenrication是 HTTP 用户代理提供用户名的一种方法 &#xff0c;它是对 Web 资源实施访问控制的最简单技术&#xff0c;它不需要 Cookie、会话标识符和登录页面。HTTP Basic身份验证使用静态的标准HTTP标头&#xff0c;这意味着 不必在预期中进行握手。 当用户代理想…...

【yaml文件的编写】

yaml文件编写 YAML语法格式写一个yaml文件demo创建资源对象查看创建的pod资源创建service服务对外提供访问并测试创建资源对象查看创建的service在浏览器输入 nodeIP:nodePort 即可访问 详解k8s中的port&#xff1a;portnodePorttargetPortcontainerPortkubectl run --dry-runc…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...