iOS自定义滚动条
- 引言
- 为什么要自定义ScrollIndictor
- 现有滚动条显示方案
- ( void) setAlpha:( CGFloat)alpha {
if ( self. superview. tag == noDisableVerticalScrollTag) {
if (alpha == 0 && self. autoresizingMask == UIViewAutoresizingFlexibleLeftMargin) {
if ( self. frame. size. width < 10 && self. frame. size. height > self. frame. size. width) {
UIScrollView *sc = ( UIScrollView*) self. superview;
if (sc. frame. size. height < sc. contentSize. height) {
return;
}
}
}
}
if ( self. superview. tag == noDisableHorizontalScrollTag) {
if (alpha == 0 && self. autoresizingMask == UIViewAutoresizingFlexibleTopMargin) {
if ( self. frame. size. height < 10 && self. frame. size. height < self. frame. size. width) {
UIScrollView *sc = ( UIScrollView*) self. superview;
if (sc. frame. size. width < sc. contentSize. width) {
return;
}
}
}
}
[ super setAlpha:alpha];
}
- 现有方案存在的问题
- 开始自定义之旅
#import "Constants.h"
@implementation BaseCollectionView{
UIView *scrollIndicatorView;
CGFloat contentSizeHeight;
}
- ( void)drawRect:( CGRect)rect {
[ self enableCustomVerticalScrollIndicatorWithColor:[ UIColor lightGrayColor]];
}
- ( void)reloadData{
[ super reloadData];
[ self setNeedsDisplay];
}
- ( UIView *)createIndicatorViewWithFrame:( CGRect) frame{
UIView *indicator = [[ UIView alloc] initWithFrame:frame];
indicator. layer. cornerRadius = ScrollIndicatorWidth/ 2.0f;
// viewScrollIndicator.alpha = 0.0f;
// viewScrollIndicator.layer.borderWidth = 1.0f;
// viewScrollIndicator.layer.borderColor = indicatorColor.CGColor;
[ self addSubview:indicator];
return indicator;
}
//Calculate the real height of scroll indictor accroding to the content size.
- ( CGFloat)getNormalScrollIndictorHeight{
CGFloat percent = self. frame. size. height / self. contentSize. height;
float normalHeight = MAX( 0.0f,(percent * self.frame.size.height));
return normalHeight;
}
- ( void)enableCustomVerticalScrollIndicatorWithColor:( UIColor *)indicatorColor
{
self. showsVerticalScrollIndicator = NO;
float height = [ self getNormalScrollIndictorHeight];
CGRect frame = CGRectMake( self. frame. size. width - ScrollIndicatorWidth - ScrollIndicatorRightSpace, 0.0f, ScrollIndicatorWidth, height);
if( scrollIndicatorView == nil){
scrollIndicatorView = [ self createIndicatorViewWithFrame:frame];
[ self addKVOObservers];
}
else{
scrollIndicatorView. frame = frame;
}
[ scrollIndicatorView setBackgroundColor:[indicatorColor colorWithAlphaComponent: 0.75]];
//If content size is larger than frame size, the indictor will be displayed.
if( self. frame. size. height >= self. contentSize. height){
scrollIndicatorView. alpha = 0;
}
else{
scrollIndicatorView. alpha = 1.0;
}
[ self refreshVerticalScrollIndicator];
}
- ( void)refreshVerticalScrollIndicator
{
if ( self. contentSize. height <= 0) {
return;
}
//Get the current frame of scroll indicator
CGRect rect = scrollIndicatorView. frame;
//Get the normal height of Indicator
float normalHeight = [ self getNormalScrollIndictorHeight];
//Calculate the real content offset ratio.
CGFloat maxConentOffset = self. contentSize. height - self. frame. size. height;
CGFloat offsetRatio = self. contentOffset. y / maxConentOffset;
//Calculate the indictor offset
CGFloat maxIndicatorOffset = ( self. frame. size. height - normalHeight);
CGFloat indicatorOffset = offsetRatio * maxIndicatorOffset;
//if scrolling out of top limitation, the scroll indictor will be compressed.
if (indicatorOffset < 0) {
rect. size. height = normalHeight + indicatorOffset;
}
//if scrolling out of bottom limitation, the scroll indictor will be compressed again.
else if(indicatorOffset > self. frame. size. height - normalHeight){
rect. size. height = normalHeight- (indicatorOffset - maxIndicatorOffset);
// indicatorOffset = self.frame.size.height - normalHeight;
}
else{
rect. size. height = normalHeight;
}
rect. origin. y = self. contentOffset. y + MAX( 0.0f,indicatorOffset);
if (rect. size. height < ScrollIndicatorMinHeight) {
rect. size. height = ScrollIndicatorMinHeight;
}
scrollIndicatorView. frame = rect;
}
- ( void)dealloc
{
[ self removeKVOObservers];
}
#pragma mark - KVO
- ( void)addKVOObservers
{
[ self addObserver: self forKeyPath: @"contentSize" options:( NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context: NULL];
[ self addObserver: self forKeyPath: @"contentOffset" options:( NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context: NULL];
}
- ( void)removeKVOObservers
{
[ self removeObserver: self forKeyPath: @"contentSize"];
[ self removeObserver: self forKeyPath: @"contentOffset"];
}
- ( void) observeValueForKeyPath: ( NSString *) keyPath ofObject: ( id) object change: ( NSDictionary *) change context: ( void *) context
{
if ( self. contentSize. width > 0.0f) {
[ self refreshVerticalScrollIndicator];
/*
UIView *viewScrollIndicator = [self getViewForHorizontalScrollIndicator];
CGRect rect = self.frame;
CGFloat pourcent = self.contentOffset.x / self.contentSize.width;
viewScrollIndicator.hidden = self.contentSize.width < self.frame.size.width;
rect.size.width = self.frame.size.width * (self.frame.size.width / self.contentSize.width);
rect.origin.x = pourcent * self.frame.size.width;
viewScrollIndicator.frame = rect;
*/
}
}
- 有待完善和改进的地方
相关文章:
iOS自定义滚动条
引言 最近一直在做数据通信相关的工作,导致了UI上的一些bug一直没有解决。这两天终于能腾出点时间大概看了一下Redmine上的bug,发现有很多bug都是与系统滚动条有关系的。所以索性就关注一下这个小小的滚动条。 为什么要自定义ScrollIndictor 原有的Scrol…...
C++知识点2:把数据写进switch case结构,和写进json结构,在使用上有什么区别
将数据存储在Switch Case结构和JSON结构中有明显的区别,它们用于不同的目的和方式。以下是它们之间的主要区别: 1、用途和结构: Switch Case结构:Switch Case是一种条件语句,通常用于根据条件执行不同的代码块。它通常…...
肖sir__linux详解__003(vim命令)
linux 文本编辑命令 作用:用于编辑一个文件 用法:vim 文件名称 或者vi (1)编辑一个存在的文档 例子:编辑一个file1文件 vim aa (2)编辑一个文件不存在,会先创建文件,再…...
瑞芯微RK3588开发板:虚拟机yolov5模型转化、开发板上python脚本调用npu并部署 全流程
目录 0. 背景1. 模型转化1.1 基础环境1.2 创建python环境1.3 将yolov5s.pt转为yolov5s.onnx1.4 将yolov5s.onnx转为yolov5s.rknn 2. 开发板部署2.1. c版本2.1. python版本(必须是python 3.9) 3. 性能测试 0. 背景 全面国产化,用瑞芯微rk3588…...
【Redis专题】RedisCluster集群运维与核心原理剖析
目录 课程内容一、Redis集群架构模型二、Redis集群架构搭建(单机搭建)2.1 在服务器下新建各个节点的配置存放目录2.2 修改配置(以redis-8001.conf为例) 三、Java代码实战四、Redis集群原理分析4.1 槽位定位算法4.2 跳转重定位4.3 …...
我眼中的《视觉测量技术基础》
为什么会写这篇博客: 首先给大家说几点:看我的自我介绍对于学习这本书没有任何帮助,如果你是为了急切的想找一个视觉测量的解决方案那可以跳过自我介绍往下看或者换一篇博客看看,如果你是刚入门想学习计算机视觉的同学࿰…...
【Cisco Packet Tracer】管理方式,命令,接口trunk,VLAN
💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …...
深入协议栈了解TCP的三次握手、四次挥手、CLOSE-WAIT、TIME-WAIT。
TCP网络编程的代码网上很多,这里就不再赘述,简单用一个图展示一下tcp网络编程的流程: 1、深入connect、listen、accept系统调用,进一步理解TCP的三次握手 这三个函数都是系统调用,我们可以分为请求连接方和被…...
接口自动化测试系列-yml管理测试用例
项目源码 目录结构及项目介绍 整体目录结构,目录说明参考 测试用例结构类似httprunner写法,可参考demo 主要核心函数 用例读取转换json import yaml import main import os def yaml_r():curpath f{main.BASE_DIR}/quality_management_logic/ops_ne…...
开源对象存储系统minio部署配置与SpringBoot客户端整合访问
文章目录 1、MinIO安装部署1.1 下载 2、管理工具2.1、图形管理工具2.2、命令管理工具2.3、Java SDK管理工具 3、MinIO Server配置参数3.1、启动参数:3.2、环境变量3.3、Root验证参数 4、MinIO Client可用命令 官方介绍: MinIO 提供高性能、与S3 兼容的对…...
Matlab之数组字符串函数汇总
一、前言 在MATLAB中,数组字符串是指由字符组成的一维数组。字符串可以包含字母、数字、标点符号和空格等字符。MATLAB提供了一些函数和操作符来创建、访问和操作字符串数组。 二、字符串数组具体怎么使用? 1、使用单引号或双引号括起来的字符序列 例…...
基于深度学习网络的火灾检测算法matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ................................................................................ load F…...
【Linux】高级IO和多路转接 | select/poll/epoll
多路转接和高级IO 咳咳,写的时候出了点问题,标点符号全乱了(批量替换了几次),干脆就把全文的逗号和句号都改成英文的了(不然代码块里面的代码都是中文标点就跑不动了) 1.高级IO 1.1 五种IO模型…...
el-select 支持多选 搜索远程数据 组件抽取
el-select 支持多选 搜索远程数据 组件抽取 使用方式 import selectView from ./components/selectView<el-form><el-form-item label"选择器"><selectView v-model"selValue" change"handleChange"></el-form-item> …...
el-table纵向垂直表头
参考:https://www.jianshu.com/p/1f38eaffd070 <el-tablestyle"width: 100%":data"getValues":show-header"false"border:cell-style"cellStyle" ><el-table-columnv-for"(item, index) in getHeaders"…...
Pinyin4j介绍和简单使用
前言 Pinyin4j是一个Java库,用于将汉字转换为拼音。它是由中国清华大学的Tsinghua University和中国科学院计算技术研究所的研究人员开发的。Pinyin4j可以用于Java应用程序中,以便在需要时将汉字转换为拼音。例如,它可以用于中文输入法、文本…...
【数据结构】查找
【数据结构】查找 数据结构中,有顺序查找、二分查找、散列查找、插值查找、斐波那契额查找 1.顺序查找 条件:待查找的元素与数组中的元素按顺序排列。算法:从数组的第一个元素开始,逐个比较,直到找到目标元素或遍历完…...
第一次面试
1.多态的原理 2.编译原理 3.HTTPS的加密原理 4.说一说C11新特性 5.平时用过哪些STL容器 6.STL的比较器 原来就是自定义工具类hhhhhh 7.函数指针用过吗 8.I/O多路复用 9.Redis 问的基本都背过,但是一紧张啥都忘了hhhhhhhhh...
Nacos配置文件更新+热更新+多环境配置共享+集群搭建
对服务配置文件 场景: 如果多个服务对应的配置文件都需要更改时,可以利用配置管理,方便对配置文件进行更新,而且是在本地配置前先读取nacos的配置文件,优先级大于本地配置文件 配置步骤 1.首先在Nacos中的配置列表中增…...
李宏毅-机器学习hw4-self-attention结构-辨别600个speaker的身份
一、慢慢分析学习pytorch中的各个模块的参数含义、使用方法、功能: 1.encoder编码器中的nhead参数: self.encoder_layer nn.TransformerEncoderLayer( d_modeld_model, dim_feedforward256, nhead2) 所以说,这个nhead的意思,就…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
Unity VR/MR开发-VR开发与传统3D开发的差异
视频讲解链接:【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
flow_controllers
关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...
