ios 瀑布流

2023-05-16

瀑布流,又称瀑布流式布局。是比较流行的一种页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。

说明(1)瀑布流每一个的宽度是一样的,都是高度不一样(2)补齐算法,哪里比较短就补哪里,不是简单的从左到右排(两列之间的差距越来越大)。

在ios中目前比较好的实现方式有2种。1.仿照UITableView思路自定义ScrollView 2.使用UICollectionView,自定义UICollectionViewLayout实现.


使用UICollectionView,自定义UICollectionViewLayout实现

ViewController.m
#import "ViewController.h"
#import "WaterflowLayout.h"

@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate, HMWaterflowLayoutDelegate>
@property (nonatomic, weak) UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray *shops;
@end

@implementation ViewController

- (NSMutableArray *)shops
{
    if (_shops == nil) {
        self.shops = [NSMutableArray array];
    }
    return _shops;
}

static NSString *const ID = @"shop";

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1.初始化数据
    NSArray *shopArray = [Shop objectArrayWithFilename:@"1.plist"];
    [self.shops addObjectsFromArray:shopArray];
    
    
    WaterflowLayout *layout = [[WaterflowLayout alloc] init];
    layout.delegate = self;
//    layout.sectionInset = UIEdgeInsetsMake(100, 20, 40, 30);
//    layout.columnMargin = 20;
//    layout.rowMargin = 30;
//    layout.columnsCount = 4;
    
    // 2.创建UICollectionView
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    collectionView.backgroundColor = [UIColor whiteColor];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    [collectionView registerNib:[UINib nibWithNibName:@"ShopCell" bundle:nil] forCellWithReuseIdentifier:ID];
    [self.view addSubview:collectionView];
    self.collectionView = collectionView;
    
    // 3.增加刷新控件
    [self.collectionView addFooterWithTarget:self action:@selector(loadMoreShops)];
}

- (void)loadMoreShops
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSArray *shopArray = [Shop objectArrayWithFilename:@"1.plist"];
        [self.shops addObjectsFromArray:shopArray];
        [self.collectionView reloadData];
        [self.collectionView footerEndRefreshing];
    });
}

#pragma mark - <WaterflowLayoutDelegate>
- (CGFloat)waterflowLayout:(WaterflowLayout *)waterflowLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath
{
    Shop *shop = self.shops[indexPath.item];
    return shop.h / shop.w * width;
}

#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.shops.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    ShopCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
    cell.shop = self.shops[indexPath.item];
    return cell;
}

@end

WaterflowLayout.h
#import <UIKit/UIKit.h>
@class WaterflowLayout;

@protocol WaterflowLayoutDelegate <NSObject>
- (CGFloat)waterflowLayout:(WaterflowLayout *)waterflowLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath;
@end

@interface WaterflowLayout : UICollectionViewLayout
@property (nonatomic, assign) UIEdgeInsets sectionInset;
/** 每一列之间的间距 */
@property (nonatomic, assign) CGFloat columnMargin;
/** 每一行之间的间距 */
@property (nonatomic, assign) CGFloat rowMargin;
/** 显示多少列 */
@property (nonatomic, assign) int columnsCount;

@property (nonatomic, weak) id<WaterflowLayoutDelegate> delegate;

@end
WaterflowLayout.m
#import "WaterflowLayout.h"

//static const CGFloat ColumnMargin = 10;
//static const CGFloat RowMargin = ColumnMargin;

@interface WaterflowLayout();
/** 这个字典用来存储每一列最大的Y值(每一列的高度) */
@property (nonatomic, strong) NSMutableDictionary *maxYDict;

/** 存放所有的布局属性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end

@implementation WaterflowLayout

- (NSMutableDictionary *)maxYDict
{
    if (!_maxYDict) {
        self.maxYDict = [[NSMutableDictionary alloc] init];
    }
    return _maxYDict;
}

- (NSMutableArray *)attrsArray
{
    if (!_attrsArray) {
        self.attrsArray = [[NSMutableArray alloc] init];
    }
    return _attrsArray;
}

- (instancetype)init
{
    if (self = [super init]) {
        self.columnMargin = 10;
        self.rowMargin = 10;
        self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
        self.columnsCount = 3;
    }
    return self;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}

/**
 *  每次布局之前的准备
 */
- (void)prepareLayout
{
    [super prepareLayout];
    
    // 1.清空最大的Y值
    for (int i = 0; i<self.columnsCount; i++) {
        NSString *column = [NSString stringWithFormat:@"%d", i];
        self.maxYDict[column] = @(self.sectionInset.top);
    }
    
    // 2.计算所有cell的属性
    [self.attrsArray removeAllObjects];
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i<count; i++) {
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        [self.attrsArray addObject:attrs];
    }
}

/**
 *  返回所有的尺寸
 */
- (CGSize)collectionViewContentSize
{
    __block NSString *maxColumn = @"0";
    [self.maxYDict enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
        if ([maxY floatValue] > [self.maxYDict[maxColumn] floatValue]) {
            maxColumn = column;
        }
    }];
    return CGSizeMake(0, [self.maxYDict[maxColumn] floatValue] + self.sectionInset.bottom);
}

/**
 *  返回indexPath这个位置Item的布局属性
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    // 假设最短的那一列的第0列
    __block NSString *minColumn = @"0";
    // 找出最短的那一列
    [self.maxYDict enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
        if ([maxY floatValue] < [self.maxYDict[minColumn] floatValue]) {
            minColumn = column;
        }
    }];
    
    // 计算尺寸
    CGFloat width = (self.collectionView.frame.size.width - self.sectionInset.left - self.sectionInset.right - (self.columnsCount - 1) * self.columnMargin)/self.columnsCount;
    CGFloat height = [self.delegate waterflowLayout:self heightForWidth:width atIndexPath:indexPath];
    
    // 计算位置
    CGFloat x = self.sectionInset.left + (width + self.columnMargin) * [minColumn intValue];
    CGFloat y = [self.maxYDict[minColumn] floatValue] + self.rowMargin;
    
    // 更新这一列的最大Y值
    self.maxYDict[minColumn] = @(y + height);
    
    // 创建属性
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attrs.frame = CGRectMake(x, y, width, height);
    return attrs;
}

/**
 *  返回rect范围内的布局属性
 */
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return self.attrsArray;
}

@end


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ios 瀑布流 的相关文章

  • ERROR: Could not build wheels for AutoROM.accept-rom-license 安装stable-baselines3错误 解决方案

    问题描述 今天在按照官方教程 1 xff0c 使用 pip install stable baselines3 extra 安装stable baseline3时 xff0c 遇到了以下bug xff1a Building wheel fo
  • 逆序打印单链表

    问题 xff1a 写一个函数 xff0c 逆序打印单链表中的数据 xff0c 假设指针L指向了单链表的开始结点 分析 可用递归的方式解决 xff0c 在表不为空的情况下先递归地逆序打印表中第一个数据之后 xff0c 然后打印第一个数据 代码
  • 右下角的角标间隔变大,调节间隔之后还是无法回到原样的解决方法

    1 首先右键这里 2 然后点击 3 找到这个模板 4 先打开 xff0c 再关掉 xff0c 反正就卡一下 xff0c 如果原先已经打开 xff0c 那么关掉再打开 xff0c 最终打不打开取决于你自己 xff08 第一次写文章 xff0c
  • win10输入法不显示候选词框解决方法

    win10输入法不显示候选词框解决方法 在Win10使用过程中 xff0c 突然发现自带输入法输入汉字时 xff0c 不显示候选词选择框时 xff0c 不必惊慌 xff0c 根据知乎网友的回答 xff0c 重启一下explorer进程就好
  • Springboot + RabbitMq 消息队列

    前言 一 RabbitMq简介 1 RabbitMq场景应用 xff0c RabbitMq特点 场景应用 以订单系统为例 xff0c 用户下单之后的业务逻辑可能包括 xff1a 生成订单 扣减库存 使用优惠券 增加积分 通知商家用户下单 发
  • freemarker 循环list 并取值

    lt if genders exists gt lt list genders as item gt lt option value 61 34 item businessNo if exists 34 gt item businessNa
  • PC端Windows 登录多个微信实现

    现在很多小伙伴都有好多微信 xff0c 但是电脑普通登录又只能等一个微信号 xff0c 怎么办呢 xff1f xff1f xff1f 别着急 我已经给大家整理好了方法 xff0c 请看下面详细步骤 xff1a 1 首先关闭并退出登录的微信
  • Java 使用注解检验手机号

    手机号校验注解类 package span class hljs keyword com span span class hljs preprocessor command span span class hljs preprocessor
  • Java8 List对象中的属性以逗号分隔转字符串

    List对象中的属性以逗号分隔转字符串 在Java 8中将集合List转变为用逗号分隔的String是非常简单的 xff0c 下面看看是如何做到 我们使用String join 函数 xff0c 给函数传递一个分隔符合一个迭代器 xff0c
  • android 获取sn序列号

    android 获取sn序列号 通过 cmd adb shell getprop 可以查看通过反射可以获取到的设备信息 通过安卓代码获取 span class token class name Class span span class t
  • fatal error LNK1181: 无法打开输入文件“kernel32.lib

    转发网址 xff1a https blog csdn net tang jin2015 article details 52879748 在VS中编译C 43 43 代码时 xff0c 报 xff1a 无法打开kernel32 lib 解决
  • ubuntu18.04 安装 ros 报错 E: Unable to locate package ros-melodic-desktop-full

    ubuntu18 04 安装 ros 报错 E Unable to locate package ros melodic desktop full 按照官网流程出现了这种问题 一般出现这种问题是系统版本和ros版本不匹配 xff0c 但是我
  • 解决pip is configured with locations that require TLS/SSL问题

    python3 7安装 xff0c 解决pip is configured with locations that require TLS SSL问题 1 安装相关依赖 yum install gcc libffi devel zlib o
  • tensorflow 如何共享网络参数

    问题描述 今天想做一个实验 xff0c 需要用到两个网络共享一套参数 初步想来 xff0c 是想利用tensorflow的变量空间和命名空间来实现的 xff0c 就像 1 和 2 中提到的那样 xff0c 后来发现涉及类初始化的关系 xff
  • STL std::sort 源码分析

    转载自http feihu me blog 2014 sgi std sort 最近在看sort源码 xff0c 看到这篇博文很好 xff0c 转发作为记录 xff0c 转载侵权联系我删除 背景 在校期间 xff0c 为了掌握这些排序算法
  • STL vector map遍历失效,顺序容器和关联容器遍历时的erase()注意事项

    1 区别 关联容器 Associative Container xff1a 通过键 key 存储和读取元素的 xff08 vector list deque stack queue heap priority queue slist xff
  • 正向,反向,闪烁流水灯

    单片机 中简单的流水灯问题 include 34 reg52 h 34 void led delay void unsigned char i j for i 61 0 i lt 200 i 43 43 for j 61 0 j lt 22
  • DS18B20温度传感源代码

    本文是对DS18b20传感器 xff0c 驱动代码的编写 include lt reg52 h gt include lt intrins h gt define uint unsigned int define uchar unsigne
  • 归并(合并)排序

    归并排序 xff0c 一种比较排序 xff0c 通过对数组中的元素进行比较得出排序结果 时间复杂度 O nlogn 空间复杂度 O n 43 O logn 排序时间与输入无关 xff0c 最佳情况 xff0c 最坏情况都是如此 稳定 原理
  • 字符串加减计算

    描述 xff1a 通过键盘输入100以内正整数的加 减运算式 xff0c 请编写一个程序输出运算结果字符串 输入字符串的格式为 xff1a 操作数1 运算符 操作数2 xff0c 操作数 与 运算符 之间以一个空格隔开 补充说明 xff1a

随机推荐

  • C/C++中,为什么字符串可以赋值给字符指针变量,单引号和双引号的区别

    本文是通过几篇转帖的文章整理而成的 xff1a http www slyar com blog c quotation marks html http www cnblogs com KingOfFreedom archive 2012 1
  • 操作符优先级全列表,一览表

    在一个表达式中可能包含多个有不同运算符连接起来的 具有不同数据类型的数据对象 xff1b 由于表达式有多种运算 xff0c 不同的运算顺序可能得出不同结果甚至出现错误运算错误 xff0c 因为当表达式中含多种运算时 xff0c 必须按一定顺
  • 枚举类的赋值和基本操作

    枚举类型的定义 枚举类型 enumeration 是C 43 43 中的一种派生数据类型 xff0c 它是由用户定义的若干枚举常量的集合 定义格式 xff1a 枚举类型的定义格式为 xff1a enum lt 类型名 gt lt 枚举常量表
  • AttributeError: module ‘gym.envs.atari‘ has no attribute ‘atari_env‘ 解决方案

    问题描述 今天在执行以下代码时 xff1a is atari 61 hasattr gym envs 39 atari 39 and isinstance env unwrapped gym envs atari atari env Ata
  • linux 防火墙常用命令

    firewalld防火墙 CentOS 7 1 查看firewalld服务状态 systemctl status firewalld 出现 Active active running 高亮显示则表示是启动状态 出现 Active inact
  • 2020年百度之星·程序设计大赛-初赛三 补题/解题

    Title Discount题解 Game题解 Permutation题解 Intersection题解 ChessAntFightGraph Discount Problem Description 学皇来到了一个餐馆吃饭 他觉得这家餐馆
  • 实习前言

    2018年4 19正式实习入职科大讯飞 xff0c 18日下午到达科大讯飞股份有限公司 第一时间拎着行李赶到提前与HR约定好的时间下午3 30地点A2综合服务中心 xff0c 进行住宿安排 入住的是所有实习生住的某工业园 xff0c 离A1
  • 科大讯飞总裁办实习第1周

    正式实习第一周 xff0c 从4月23日开始 根据上周五灿武哥给的邮件任务为导向 xff0c 开始学习输入 对于ibpms设计器的学习 xff0c 灿武哥给了3份学习文档资料 xff0c 其实是针对不同使用人群的使用手册 xff08 针对管
  • 科大讯飞总裁办实习第二周

    五一放假回来第一天上班就是周三啦 xff0c 这周只需要上三天班 xff0c 在上下午间隙时间 xff0c 研读流程管理使用手册 xff0c 整理以下几个概念知识点 流程管理的目标 xff1a 使企业的流程运作从整体上更加顺畅 协调 xff
  • 科大讯飞总裁办实习第三周

    5 7号周一上班收到的第一个消息 xff1a 带我流程的瑞哥请假三天 经理在群里说这块找张杰确认 xff0c 突然就一个重担砸到我的身上了 之前一直站在瑞哥的身后 xff0c 需求来自于瑞哥直接吩咐 xff0c 现在直接需求来源于项目推进
  • 科大讯飞总裁办实习第5周

    5 21 周一上班首先补充试点推广流程文件详细信息 xff0c 上午共导出5 xff0c 6 xff0c 7 xff0c 8四个流程文件 上午经理在群里下达最后截止日期 xff0c 所有的规范 xff0c 流程 xff0c 文件都要周五之前
  • 科大讯飞实习第八周日志

    0611早上欲打算与业务部门尽快完善流程 xff0c 大早上联系那边的流程设计人员 xff0c 不巧的是他早上有会 xff0c 然后就自己看华为变革及管理流程框架 xff0c 下午一点半和宇婴哥一起参加了销委会商机研讨会议 xff0c 回来
  • PostgreSQL数据库导出建表语句的方法

    pg dump U postgres d dbname s gt sql txt
  • spyder导入tensorflow包

    一 xff0e spyder介绍 Anaconda中自带的集成开发环境用于科学计算还是蛮好的 xff0e 它和其他的Python开发环境相比 xff0c 它最大的优点就是模仿MATLAB的 工作空间 的功能 xff0c 可以很方便地观察和修
  • ValueError: Disable frame-skipping in the original env. 解决方案

    问题描述 今天试图在Atari上运行以下代码时 xff0c 出现了题目中的bug xff1a env 61 AtariPreprocessing env grayscale obs 61 True scale obs 61 True ter
  • OpenKylin适配和虚拟打印机

    最近在测国产OS客户端部分 首先客户端程序在CentOS全部使用没毛病 xff0c 但是CentOS桌面体验比较差 然后就试了UOS xff0c 在UOS上测试到打印这块花了很多时间 xff0c 碰到问题是CUPS有反应 xff0c 但是没
  • 基础命令整理

    1 who显示的是当前真正登录系统中的用户 16 05 59 root 64 localhost who ZT tty2 2021 11 03 10 45 tty2 ZT pts 1 2021 11 05 08 28 10 0 0 1 2
  • ubuntu apache2 配置安装ssl证书,https

    1 申请免费阿里证书 2 配置证书 在这里 xff0c 我假设你已经会配置基本的 etc apache2 sites available 000 default conf这个文件来达到已经可以通过 http 的方式来访问你的站点 在 etc
  • 今日头条2018校招笔试题之字符串的问题

    今日头条 xff0c 很干脆 xff0c 直接就四个编程 xff0c 一个改错 做的很烂 xff0c 只能来写一个题 字符串S由小写字母构成 xff0c 长度为N xff0c 定义一种操作 xff0c 每次都可以挑选字符串中任意的两个相邻字
  • ios 瀑布流

    瀑布流 xff0c 又称瀑布流式布局 是比较流行的一种 页面布局 xff0c 视觉表现为参差不齐的多栏布局 xff0c 随着页面滚动条向下滚动 xff0c 这种布局还会不断加载 数据块并附加至当前尾部 说明 xff1a xff08 1 xf