IOS 18 发现界面(UITableView)Banner轮播图实现

news/2024/9/17 7:10:22/ 标签: ios, UITableView

发现界面完整效果

本文实现Banner轮播图效果

文章基于IOS 17 基于UITabBarController实现首页TabBar继续实现发现界面

实现逻辑

从发现界面的效果图可以看出,发现界面是一个列表,列表包含了不同的Item,我们可以将 banner部分看成是列表的一个Item(Cell),列表使用UITableView来实现。

封装BaseLogicController

由于返现界面控制器DiscoveryController和多个界面控制器都继承自BaseLogicController,而且列表UITableView在多个控制器上都需要用到,故将列表使用UITableView统一编写在父类BaseLogicController上,方便统一管理和使用。

实现代码:

    /// 初始化TableView,四边都在安全区内func initTableViewSafeArea() {//外面添加一层容器,是方便在真实内容控件前后添加内容initLinearLayoutSafeArea()//tableViewcreateTableView()container.addSubview(tableView)}/// 创建TableView,不会添加到任何布局func createTableView() {tableView = ViewFactoryUtil.tableView()tableView.delegate = selftableView.dataSource = self}

由于UITableView在多个地方会使用到,故统一在ViewFactoryUtil类上使用静态方法创建tableView,方便管理和复用。

    static func tableView() -> UITableView {let r = QMUITableView()r.backgroundColor = .clear//去掉没有数据cell的分割线r.tableFooterView = UIView()//去掉默认分割线r.separatorStyle = .none//修复默认分割线,向右偏移问题r.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)r.tg_width.equal(.fill)r.tg_height.equal(.fill)//设置所有cell的高度为高度自适应,如果cell高度是动态的请这么设置。 如果不同的cell有差异那么可以通过实现协议方法-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath//如果您最低要支持到iOS7那么请您实现-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath方法来代替这个属性的设置。r.rowHeight = UITableView.automaticDimensionr.estimatedRowHeight = UITableView.automaticDimension//不显示滚动条r.showsVerticalScrollIndicator = falser.allowsSelection = true//分割线颜色r.separatorColor = .colorDividerreturn r}

BaseLogicController完整代码:

//
//  BaseLogicController.swift
//  MyCloudMusic
//
//  Created by jin on 2024/8/19.
//import UIKit//提供类似Android中更高层级布局框架
import TangramKitclass BaseLogicController: BaseCommonController {/// 根容器var rootContainer: TGBaseLayout!/// 头部容器var superHeaderContainer: TGBaseLayout!var superHeaderContentContainer: TGBaseLayout!/// 容器var container: TGBaseLayout!/// 底部容器var superFooterContainer: TGBaseLayout!var superFooterContentContainer: TGBaseLayout!/// TableViewvar tableView: UITableView!lazy var datum: [Any] = {var r :[Any] = []return r}()/// 初始化RelativeLayout容器,四边都在安全区内func initRelativeLayoutSafeArea() {initLinearLayout()//headerinitHeaderContainer()//中间内容容器container = TGRelativeLayout()container.tg_width.equal(.fill)container.tg_height.equal(.fill)container.backgroundColor = .clearrootContainer.addSubview(container)//footerinitFooterContainer()}/// 初始化垂直方向LinearLayout容器,四边都在安全区内func initLinearLayoutSafeArea(){initLinearLayout()//headerinitHeaderContainer()//中间内容容器container = TGLinearLayout(.vert)container.tg_width.equal(.fill)container.tg_height.equal(.fill)container.backgroundColor = .clearrootContainer.addSubview(container)//footerinitFooterContainer()}/// 初始化TableView,四边都在安全区内func initTableViewSafeArea() {//外面添加一层容器,是方便在真实内容控件前后添加内容initLinearLayoutSafeArea()//tableViewcreateTableView()container.addSubview(tableView)}/// 创建TableView,不会添加到任何布局func createTableView() {tableView = ViewFactoryUtil.tableView()tableView.delegate = selftableView.dataSource = self}/// 使用默认分割线func initDefaultTableViewDivider() {tableView.separatorStyle = .singleLine}/// 初始化垂直方向LinearLayout容器func initLinearLayout() {rootContainer = TGLinearLayout(.vert)rootContainer.tg_width.equal(.fill)rootContainer.tg_height.equal(.fill)rootContainer.backgroundColor = .clearview.addSubview(rootContainer)}/// 头部容器,安全区外,一般用来设置头部到安全区外背景颜色func initHeaderContainer() {superHeaderContainer = TGLinearLayout(.vert)superHeaderContainer.tg_width.equal(.fill)superHeaderContainer.tg_height.equal(.wrap)superHeaderContainer.backgroundColor = .clear//头部内容容器,安全区内superHeaderContentContainer = TGLinearLayout(.vert)superHeaderContentContainer.tg_height.equal(.wrap)superHeaderContentContainer.tg_top.equal(TGLayoutPos.tg_safeAreaMargin)superHeaderContentContainer.tg_leading.equal(TGLayoutPos.tg_safeAreaMargin)superHeaderContentContainer.tg_trailing.equal(TGLayoutPos.tg_safeAreaMargin)superHeaderContentContainer.backgroundColor = .clearsuperHeaderContainer.addSubview(superHeaderContentContainer)rootContainer.addSubview(superHeaderContainer)}func initFooterContainer() {superFooterContainer = TGLinearLayout(.vert)superFooterContainer.tg_width.equal(.fill)superFooterContainer.tg_height.equal(.wrap)superFooterContainer.backgroundColor = .clear//底部内容容器,安全区内superFooterContentContainer = TGLinearLayout(.vert)superFooterContentContainer.tg_height.equal(.wrap)superFooterContentContainer.tg_bottom.equal(TGLayoutPos.tg_safeAreaMargin)superFooterContentContainer.tg_leading.equal(TGLayoutPos.tg_safeAreaMargin)superFooterContentContainer.tg_trailing.equal(TGLayoutPos.tg_safeAreaMargin)superFooterContentContainer.backgroundColor = .clearsuperFooterContainer.addSubview(superFooterContentContainer)rootContainer.addSubview(superFooterContainer)}override func initViews() {super.initViews()setBackgroundColor(.colorBackground)}
}//TableView数据源和代理
extension BaseLogicController:UITableViewDataSource,UITableViewDelegate{func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return datum.count}func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {return UITableViewCell()}
}

列表UITableView实现流程

在IOS上,要实现列表UITableView的显示效果,需要以下三个流程:

1.创建UITableView

2.创建Cell,及在使用UITableView的Controller控制器上注册Cell;

3.获取data列表数据,并调用UITableView的reloadData(),将数据更新到列表;

4.将data的Item数据绑定UITableView的每一个Cell。

1)创建UITableView

UITableView的创建已经在父类BaseLogicController上统一实现。下面重写DiscoveryController的initViews()方法,调用父类BaseLogicController创建UITableView

    override func initViews() {super.initViews()setBackgroundColor(.colorBackgroundLight)//初始化TableView结构initTableViewSafeArea()}

2)创建和注册Cell

创建UITableViewCell,由于UITableViewCell在多处使用,故统一封装BaseTableViewCell来实现。默认定义个水平方向的TGLinearLayout来实现,方向也定义了方法getContainerOrientation(),子类可以重写该方法实现布局方向。为了让item自动计算高度,重写了方法systemLayoutSizeFitting(),本文使用的是纯代码和TangramKitUI框架开发,不了解的可以看前面文章:IOS 02 SnapKit 纯代码开发 和 IOS 04 TangramKit 纯代码开发

BaseTableViewCell实现:
//
//  BaseTableViewCell.swift
//  通用TableViewCell
//
//  Created by jin on 2024/8/27.
//import UIKit//提供类似Android中更高层级布局框架
import TangramKitclass BaseTableViewCell:UITableViewCell{//对于需要动态评估高度的UITableViewCell来说可以把布局视图暴露出来。用于高度评估和边界线处理。以及事件处理的设置。var container:TGBaseLayout!override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {super.init(style: style, reuseIdentifier: reuseIdentifier)innerInit()}required init?(coder: NSCoder) {super.init(coder: coder)innerInit()}func innerInit() {initViews()initDatum()initListeners()}/// 找控件func initViews() {//背景透明backgroundColor = .clearcontentView.backgroundColor = .clear//去掉默认的选中颜色selectionStyle = .none//根容器container = TGLinearLayout(getContainerOrientation())container.tg_width.equal(.fill)container.tg_height.equal(.wrap)container.tg_space = PADDING_MEDDLEcontentView.addSubview(container)}func initDatum() {}func initListeners() {}/// 获取根容器布局方向func getContainerOrientation() -> TGOrientation {return .horz}/// 使用TangramKit后,让item自动计算高度,要重写该方法/// - Parameters:///   - targetSize: <#targetSize description#>///   - horizontalFittingPriority: <#horizontalFittingPriority description#>///   - verticalFittingPriority: <#verticalFittingPriority description#>/// - Returns: <#description#>override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {return self.container.systemLayoutSizeFitting(targetSize)}
}
轮播图Cell BannerCell实现

轮播图实现效果稍微复杂,我们先显示一个图片来代替,等UITableView的完整效果实现出来,我们再来实现Cell的轮播图效果,图片BannerCell实现:

//
//  BannerCell.swift
//  轮播图cell
//
//  Created by jin on 2024/8/27.
//import UIKit//提供类似Android中更高层级布局框架
import TangramKitclass BannerCell:BaseTableViewCell{var bannerData:BannerData!var datum:[String] = []override func initViews() {super.initViews()//底部的距离,由下一个控件设置,除非不方便设置container.tg_padding = UIEdgeInsets(top: 16, left: 16, bottom: 0, right: 16)//轮播图let imageView = UIImageView()imageView.tg_width.equal(.fill)imageView.tg_height.equal(UIScreen.main.bounds.width * 0.389)imageView.image = R.image.placeholder()container.addSubview(imageView)}/// 绑定数据/// - Parameter data: <#data description#>func bind(_ data:BannerData) {bannerData = data}
}
注册BannerCell 

重写DiscoveryController的initViews()方法,注册cell。

    override func initViews() {super.initViews()setBackgroundColor(.colorBackgroundLight)//初始化TableView结构initTableViewSafeArea()//注册celltableView.register(BannerCell.self, forCellReuseIdentifier: Constant.CELL)}

3)获取data列表数据

定义Banner数据模型BannerData

//
//  BannerData.swift
//  发现界面轮播图模型
//
//  Created by jin on 2024/8/27.
//import Foundationclass BannerData{var data:Array<Ad>!init(data: Array<Ad>!) {self.data = data}
}

在DiscoveryController控制器中,重写initDatum()方法,通过接口获取后端Banner数据。获取完数据后,一定要调用tableView.reloadData(),列表才会真正加载数据。网络请求还不熟悉的可以看文章:IOS 14 封装网络请求框架

    override func initDatum() {super.initDatum()loadData()}func loadData() {DefaultRepository.shared.bannerAds().subscribeSuccess { [weak self] data in//清除原来的数据self?.datum.removeAll()//添加轮播图self?.datum.append(BannerData(data:data.data!.data!))self?.tableView.reloadData()}.disposed(by: rx.disposeBag)}

由于父类BaseLogicController已统一实现TableView数据源和代理扩展,将数据绑定列表,子类DiscoveryController不需要再操作。

//TableView数据源和代理
extension BaseLogicController:UITableViewDataSource,UITableViewDelegate{func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return datum.count}func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {return UITableViewCell()}
}

4)Item数据绑定Cell

DiscoveryController控制器重写父类的扩展 cellForRowAt方法,创建对应的Cell,并将Item数据绑定到Cell。

extension DiscoveryController{// 返回当前位置cell/// - Parameters:///   - tableView: <#tableView description#>///   - indexPath: <#indexPath description#>/// - Returns: <#description#>override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {let data = datum[indexPath.row]//获取当前Cell的类型let type = typeForItemAtData(data)switch(type){default://banner//取出一个Celllet cell = tableView.dequeueReusableCell(withIdentifier:  Constant.CELL, for: indexPath) as! BannerCell//绑定数据cell.bind(data as! BannerData)return cell}}
}

此时已实现Banner图片效果:

 

Banner轮播图实现 

Banner轮播图实现是使用的一个第三方框架YJBannerView,这里也可以替换成自己常用的三方框架。完整实现代码如下:

//
//  BannerCell.swift
//  轮播图cell
//
//  Created by jin on 2024/8/27.
//import UIKit//提供类似Android中更高层级布局框架
import TangramKitclass BannerCell:BaseTableViewCell{var bannerView:YJBannerView!var bannerData:BannerData!var datum:[String] = []var bannerClick:((Ad)->Void)!override func initViews() {super.initViews()//底部的距离,由下一个控件设置,除非不方便设置container.tg_padding = UIEdgeInsets(top: 16, left: 16, bottom: 0, right: 16)//轮播图bannerView = YJBannerView()bannerView.backgroundColor = .clearbannerView.dataSource = selfbannerView.delegate = selfbannerView.tg_width.equal(.fill)//SCREEN_WIDTH是QMUI提供的宏//直接在initViews里面这样获取self.contentView.frame.size.width是默认值//而不是应用了自动布局后的值bannerView.tg_height.equal(UIScreen.main.bounds.width * 0.389)bannerView.clipsToBounds = truebannerView.layer.cornerRadius = 10//设置如果找不到图片显示的图片bannerView.emptyImage=R.image.placeholder()//设置占位图bannerView.placeholderImage=R.image.placeholder()//设置轮播图内部显示图片的时候调用什么方法bannerView.bannerViewSelectorString="sd_setImageWithURL:placeholderImage:"//设置指示器默认颜色bannerView.pageControlNormalColor = .black80//高亮的颜色bannerView.pageControlHighlightColor = .colorPrimarycontainer.addSubview(bannerView)}/// 绑定数据/// - Parameter data: <#data description#>func bind(_ data:BannerData) {bannerData = data//清除原来的数据datum.removeAll()//循环每一个广告//取出广告的地址//放到一个数组中for it in data.data {datum.append(ResourceUtil.resourceUri(it.icon))}//通知轮播图框架从新加载数据bannerView.reloadData()}
}// MARK: - banner数据源和代理
extension BannerCell:YJBannerViewDataSource,YJBannerViewDelegate{/// 返回BannerView要显示的数据////// - Parameter bannerView: <#bannerView description#>/// - Returns: <#return value description#>func bannerViewImages(_ bannerView: YJBannerView!) -> [Any]! {return datum}/// 自定义Cell/// 复写该方法的目的是/// 设置图片的缩放模式////// - Parameters:///   - bannerView: <#bannerView description#>///   - customCell: <#customCell description#>///   - index: <#index description#>/// - Returns: <#return value description#>func bannerView(_ bannerView: YJBannerView!, customCell: UICollectionViewCell!, index: Int) -> UICollectionViewCell! {//将cell类型转为YJBannerViewCelllet cell = customCell as! YJBannerViewCell//设置图片的缩放模式为//从中心填充//多余的裁剪掉cell.showImageViewContentMode = .scaleAspectFillreturn cell}/// banner点击回调方法////// - Parameters:///   - bannerView: <#bannerView description#>///   - index: <#index description#>func bannerView(_ bannerView: YJBannerView!, didSelectItemAt index: Int) {//获取当前点击的广告对象let r = bannerData.data[index]bannerClick(r)}
}

banner轮播图效果实现完成:

 

本文主要学习UITableView的使用,后续内容会慢慢实现 发现页面的全部内容。


http://www.ppmy.cn/news/1522638.html

相关文章

分享基于PDF.JS的移动端PDF阅读器代码

一、前言 在之前的文章《分享基于PDF.js的pdf阅读器代码》里提到了PC端基于PDF.js的阅读器&#xff0c;本文将提供针对移动端的版本。 二、pdfViewer 为了能够直接使用&#xff0c;这里分享一下经过简单修改后能直接使用的pdfViewer代码&#xff1a; pdfViewer代码目录&…

webpack - 五大核心概念和基本配置(打包一个简单HTML页面)

// 五大核心概念 1. entry&#xff08;入口&#xff09; 指示Webpack从哪个文件开始打包2. output&#xff08;输出&#xff09; 指示Webpack打包完的文件输出到哪里去&#xff0c;如何命名等3. loader&#xff08;加载器&#xff09; webpack本身只能处理js&#xff0c;json等…

如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?

在当今的互联网世界中&#xff0c;网络请求是数据交换的基础。无论是在开发Web应用程序、自动化测试还是进行数据抓取&#xff0c;掌握如何发送网络请求是一项基本技能。Lua&#xff0c;作为一种轻量级、高性能的脚本语言&#xff0c;经常被用于这些场景。本文将详细介绍如何使…

C++11新增特性:列表初始化(std::initializer_list) decltype、auto、nullptr、范围for

C11新增特性&#xff1a;列表初始化&#xff08;std::initializer_list&#xff09;& decltype、auto、nullptr、范围for 一、C11新增统一初始化方式1.1 新增方式1.2 初始化容器底层原理&#xff08;std::initializer_list&#xff09; 二、新增声明2.1 decltype2.3 auto &…

uniapp设置微信小程序的交互反馈

链接&#xff1a;uni.showToast(OBJECT) | uni-app官网 (dcloud.net.cn) 设置操作成功的弹窗&#xff1a; title是我们弹窗提示的文字 showToast是我们在加载的时候进入就会弹出的提示。 2.设置失败的提示窗口和标签 icon&#xff1a;error是设置我们失败的logo 设置的文字上…

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 哈希表这里用哈希集合Set()实现。左指针i&#xff0c;右指针j&#xff0c;从头遍历数组&#xff0c;若j指针指向的元素不…

浙大数据结构:02-线性结构4 Pop Sequence

这道题我们采用数组来模拟堆栈和队列。 简单说一下大致思路&#xff0c;我们用栈来存1234.....&#xff0c;队列来存输入的一组数据&#xff0c;栈与队列进行匹配&#xff0c;相同就pop 机翻 1、条件准备 stk是栈&#xff0c;que是队列。 tt指向的是栈中下标&#xff0c;fr…

DPDK基础入门(五):报文转发

网络处理模块划分 Packet Input: 接收数据包&#xff0c;将其引入处理流程。Pre-processing: 对数据包进行初步处理&#xff0c;例如基本的检查和标记。Input Classification: 细化数据包的分类&#xff0c;例如基于协议或流进行分流。Ingress Queuing: 将数据包放入队列中进行…

Dubbo 安全方面措施

在分布式系统中&#xff0c;安全性是一个至关重要的因素&#xff0c;特别是对于像 Dubbo 这样的高性能 RPC 框架&#xff0c;确保服务的安全性和数据传输的完整性至关重要。Dubbo 作为一个成熟的分布式服务框架&#xff0c;在安全性方面提供了多种措施和配置选项&#xff0c;帮…

uniapp 给画作生成画框

<template><ax-page class"privateCustom"><gui-page :customHeader"true" ref"guipage"><template #gHeader><aHeader title"个性定制" :showTitle"true" back"2"></aHeader&g…

深度学习与大模型第4课:使用多种模型在Pima印度糖尿病数据集上的分类效果评估

文章目录 技术博客&#xff1a;使用多种模型在Pima印度糖尿病数据集上的分类效果评估数据集介绍数据预处理模型一&#xff1a;逻辑斯谛回归&#xff08;Logistic Regression&#xff09;模型二&#xff1a;支持向量机&#xff08;SVM&#xff09;模型三&#xff1a;决策树&…

1、正则表达式

1、正则表达式是一种用于描述文本模式的工具。它是由字符和特殊符号组成的字符串&#xff0c;描述了模式的重复或者多个字符&#xff0c;于是就可以按照某种模式匹配一系列有相似特征的字符串。它主要的作用是将文本用某种可被计算机识别的模式表现出来&#xff0c;为高级的文本…

Helm Deploy Online Rancher v2.9.1

文章目录 准备安装查看下载 准备 $ kubectl get node NAME STATUS ROLES AGE VERSION kube-master01 Ready control-plane 19d v1.29.5 kube-node01 Ready <none> 19d v1.29.5 kube-node02 Ready <none&…

Tekton简介,安装和构建最简单ci/cd

简介 Tekton是一种基于k8的支持CI/CD的operator。 说到持续集成&#xff0c;我们比较熟悉的有jenkins&#xff0c;gitlab ci等&#xff0c;但只有Tekton是云原生的。 既然Tekton是一种operator&#xff0c;那就必须了解它的CRD&#xff0c;然后我们定义CR&#xff0c;让Tekt…

WebAPI (一)DOM树、DOM对象,操作元素样式(style className,classList)。表单元素属性。自定义属性。间歇函数定时器

文章目录 Web API基本认知一、 变量声明二、 DOM1. DOM 树2. DOM对象3. 获取DOM对象(1)、选择匹配的第一个元素(2)、选择匹配多个元素 三、 操作元素1. 操作元素内容2. 操作元素属性(1)、常用属性&#xff08;href之类的&#xff09;(2)、通过style属性操作CSS(3)、通过类名(cl…

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、…

golang hertz框架入门

两种模式新建项目 1、手动新建项目 2、使用hz工具新建项目 一、手动创建项目&#xff0c;并拉取框架 1、新建项目目录 hertz_demo_w 2、在项目跟目录新建main.go 文件 package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.…

API安全 | 发现API的5个小tips

在安全测试目标时&#xff0c;最有趣的测试部分是它的 API。API 是动态的&#xff0c;它们比应用程序的其他部分更新得更频繁&#xff0c;并且负责许多后端繁重的工作。在现代应用程序中&#xff0c;我们通常会看到 REST API&#xff0c;但也会看到其他形式&#xff0c;例如 Gr…

C# 使用国密SM4加密解密

首先需第三方Nuget包&#xff1a;Portable.BouncyCastle &#xff08;源码来自http://www.bouncycastle.org/csharp/&#xff09;&#xff0c;支持.NET 4,.NET Standard 2.0 目录 使用BouncyCastle指定填充方案 零填充&#xff08;Zero Padding&#xff09; PKCS7填充&…

MariaDB基本知识汇总

/* MariaDB 1、视图 2、临时表 3、自定义函数 4、存储过程 5、触发器 6、游标 7、变量声明与赋值 8、常用函数&#xff08;日期格式&#xff0c;Guid&#xff0c;判断&#xff0c;循环&#xff0c;XML格式操作&#xff09; 9、动态执行SQL 语句 10、开启执行计划 11、创建登录M…