「iOS」UI——无限轮播图实现与UIPageControl运用

「OC」UI

文章目录

  • 「OC」UI
    • 无限轮播图的实现以及UIPageControl的实际运用
      • 明确要求
      • 简单滚动视图的实现
        • UIPageControl的实现
        • 设置NSTimer实现自动移动
        • 补充实现
    • 进行无限滚动视图的修改
      • 思路
      • 实现
    • 完整代码展示

无限轮播图的实现以及UIPageControl的实际运用

明确要求

我们要实现一个能够进行无限滚动播放的视图程序,首先需要实现的是一个简单的滚动图片视图,在视图之中添加相关的UIPageControl的控件,让我们能够知道我们当前图片是处在滚动页面的哪个位置上。在实现简单滚动视图的基础上进行修改,使其实现无限轮播的功能。

简单滚动视图的实现

提到滚动视图,我们就会想起刚刚学习的UIScrollView,使用UIImageVIew布局在UIScrollView之中,我们写给其写个函数将UIScrollView的框架封装起来

- (void)setupScrollView {
    //进行初始化操作
    self.scrollView = [[UIScrollView alloc] init];
    self.scrollView.frame = CGRectMake(0, 80, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 320);
  //设置整页滚动
    self.scrollView.pagingEnabled = YES;
    self.scrollView.scrollEnabled = YES;
    self.scrollView.delegate = self;
  //由于我们使用UIPageControl所以,
    self.scrollView.showsHorizontalScrollIndicator = NO;
    
    CGFloat h = [UIScreen mainScreen].bounds.size.height - 320;
    CGFloat w = [UIScreen mainScreen].bounds.size.width;
    self.scrollView.contentSize = CGSizeMake(w * 5, h); // 五个图片设置五个宽度
    
    for (int i = 0; i < 5; i++) {
        NSString *name = [NSString stringWithFormat:@"%d.jpg", i];
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:name]];
        imageView.frame = CGRectMake(i * w, 0, w, h);
        [self.scrollView addSubview:imageView];
    }
    [self.view addSubview:self.scrollView];
}
UIPageControl的实现

对于UIPageControl的实现十分简单,实现如下

- (void)setupPageControl {
    self.pageControl = [[UIPageControl alloc] init];
    //设置PageControl与滚动视图对齐
    self.pageControl.frame = CGRectMake(0, CGRectGetMaxY(self.scrollView.frame) - 20, CGRectGetWidth(self.scrollView.frame), 20);
    self.pageControl.numberOfPages = 5;
    //设置初始页面
    self.pageControl.currentPage = 0;
    self.pageControl.pageIndicatorTintColor = [UIColor redColor];
    self.pageControl.currentPageIndicatorTintColor = [UIColor blueColor];
    self.pageControl.userInteractionEnabled = NO;
    [self.view addSubview:self.pageControl];
}

那么对于当前位置的UIPageControl显示的变化,我们也要控制下面显示的变化,对于我们显示的变化,我们可以进行更深入的控制,,我们在当前偏移量添加半个视图宽度,这样当我们当前图片偏移量超过半个宽度之后,UIPageControl的位置就会发生变化,就会显得我们的显示更加智能,我们将代码写在scrollViewDidScroll:(UIScrollView *)scrollView之中,这个函数是当UIScrollView发生滚动就会调用。

(void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat offSetX = scrollView.contentOffset.x;
    //求取当前页
    CGFloat pageWidth = scrollView.frame.size.width;
    int currentPage = floor((offSetX - pageWidth / 2) / pageWidth) + 1;
    //控制UIPageControl 的当前页
    self.pageControl.currentPage = currentPage;
}

设置NSTimer实现自动移动

我们可以设置一个定时器,使其不断进行视图的替换,我们设置间隔为2秒,每两秒调用翻页的方法,对滚动视图界面进行翻页,需要特殊判断的是,当移动到最后的时候,我们需要将其调到第一个视图。

- (void)setupTimer {
    //创建定时器
	_timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage) userInfo:nil repeats:YES];

}

- (void)nextPage {
    NSInteger page = self.pageControl.currentPage;
   	if (page == self.pageControl.numberOfPages - 1) {
    		page = 0;
    } else {
    	 	page++;
    }
    CGFloat offSetX = page * self.scrollView.frame.size.width;
    [self.scrollView setContentOffset:CGPointMake(offSetX, 0) animated:YES];

}

补充实现

当我们在使用鼠标对滚动视图进行抓取移动时,由于定时器的存在,视图仍然会进行移动,因此,我们为了方便操作,我们可以在当鼠标进行点击时,将计时器取消,当鼠标结束拖拽时重新创建计时器,我们就需要用上两个函数。

  • -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView :当开始进行拖拽
  • -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate:当拖拽结束时

实现如下:

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    if ([self.timer isValid]) {
        [self.timer invalidate];
        self.timer = nil;
    }
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    if (![_timer isValid]) {
    _timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
    }
}

Jun-19-2024 00-02-48

进行无限滚动视图的修改

思路

我们在对其进行无限滚动视图的修改,首先我们要了解无限滚动视图的如何进行实现的,我们需要在原本的滚动视图之中,左右各自添加一个新的视图。这是为什么呢?

我们将五张图片简化为三张图片,形状如下:

image-20240618235611013

通过观察我们的原理图,我们可以很清晰的了解我们需要实现的功能:当我们的滚动视图处于第二张视图的位置(第一张pic1的位置)时,向前滑动所展示的图片是png3;当处于倒数第二张视图的位置(第二张pic3)时,向后滚动要展现的图片是pic1。了解轮播图是这个结构之后,我们就要思考如何去实现这个功能,我们使用偏移量进行解决,当我们即将翻到从pic1翻到pic3(情况1)或者pic3直接翻到pic1(情况2)的时候,我们从偏移量之中读取这种情况,然后进行滚动视图的变化,当情况1出现时,就会将当前视图从pic1的位置直接跳到标红的pic3的位置:情况2同理视图就会从pic3跳转到标红的pic1。由于动画效果的存在,我们进行滑动的时候,并不会在显示之中发现视图变化的不自然。

实现

我们对以上的程序进行修改,由于是五张图片,那么我们在左右两边多添加两张图片,我们称第一张和最后一张图片为实现无限滚动视图的假图,我们对框架内的函数进行修改,此外我们还需要将滚动视图的定位定位至pic1处。

  • ScrollView
- (void)setupScrollView {
    self.scrollView = [[UIScrollView alloc] init];
    self.scrollView.frame = CGRectMake(0, 80, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 320);
    self.scrollView.pagingEnabled = YES;
    self.scrollView.scrollEnabled = YES;
    self.scrollView.delegate = self;
    self.scrollView.showsHorizontalScrollIndicator = NO;
    
    CGFloat h = [UIScreen mainScreen].bounds.size.height - 320;
    CGFloat w = [UIScreen mainScreen].bounds.size.width;
    self.scrollView.contentSize = CGSizeMake(w * 7, h); // 包括两个额外的页面
    
    for (int i = 0; i < 7; i++) {
        NSString *name;
        if (i == 0) {
            name = @"5.jpg"; // 第一页前面的假页
        } else if (i == 6) {
            name = @"1.jpg"; // 最后一页后面的假页
        } else {
            name = [NSString stringWithFormat:@"%d.jpg", i];
        }
        
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:name]];
        imageView.frame = CGRectMake(i * w, 0, w, h);
        [self.scrollView addSubview:imageView];
    }
    
    // 设置默认显示的页面(实际第一页)
    [self.scrollView setContentOffset:CGPointMake(w, 0) animated:NO];
    [self.view addSubview:self.scrollView];
}
  • scrollViewDidScroll:(UIScrollView *)scrollView在这个函数之中的内容就是我们实现无限滚动视图的关键,请看代码
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat offsetX = scrollView.contentOffset.x;
  //滚动视图的显示宽度
    CGFloat pageWidth = scrollView.frame.size.width;
    
  //新添加的内容
    if (offsetX >= pageWidth * 6) {
        // 滚动到假的最后一页,瞬间跳到实际第一页
        [self.scrollView setContentOffset:CGPointMake(pageWidth, 0) animated:NO];
    } else if (offsetX <= 0) {
        // 滚动到假的第一页,瞬间跳到实际最后一页
        [self.scrollView setContentOffset:CGPointMake(pageWidth * 5, 0) animated:NO];
    }
    
    // 更新UIPageControl的当前页
    NSInteger currentPage = (scrollView.contentOffset.x + pageWidth / 2) / pageWidth;
  //当翻到假页时对pagecontrol进行修改,使其符合要求
    if (currentPage == 0) {
        self.pageControl.currentPage = 4;
    } else if (currentPage == 6) {
        self.pageControl.currentPage = 0;
    } else {
        self.pageControl.currentPage = currentPage - 1;
    }
}
  • 在我们使用翻页的方法也需要进行一点点的改变,我们要将page的偏移量加一。在这里还有一个小细节,由于无限滚动视图的实现,我们不再需要对最后一张视图进行特判,我们只需要在每次调用的时候然page++就可以了。
- (void)nextPage {
    NSInteger page = self.pageControl.currentPage;
    if (page == self.pageControl.numberOfPages - 1) {
        page = 0;
    } else {
        page++;
    }
    
    CGFloat offsetX = (page + 1) * self.scrollView.frame.size.width; // 偏移量加1,因为第1页是假的
    [self.scrollView setContentOffset:CGPointMake(offsetX, 0) animated:YES];
}

完整代码展示

#import "ViewController.h"

@interface ViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIPageControl *pageControl;
@property (nonatomic, strong) NSTimer *timer;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setupScrollView];
    [self setupPageControl];
    [self setupTimer];
}

- (void)setupScrollView {
    self.scrollView = [[UIScrollView alloc] init];
    self.scrollView.frame = CGRectMake(0, 80, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 320);
    self.scrollView.pagingEnabled = YES;
    self.scrollView.scrollEnabled = YES;
    self.scrollView.delegate = self;
    self.scrollView.showsHorizontalScrollIndicator = NO;
    
    CGFloat h = [UIScreen mainScreen].bounds.size.height - 320;
    CGFloat w = [UIScreen mainScreen].bounds.size.width;
    self.scrollView.contentSize = CGSizeMake(w * 7, h); // 包括两个额外的页面
    
    for (int i = 0; i < 7; i++) {
        NSString *name;
        if (i == 0) {
            name = @"5.jpg"; // 第一页前面的假页
        } else if (i == 6) {
            name = @"1.jpg"; // 最后一页后面的假页
        } else {
            name = [NSString stringWithFormat:@"%d.jpg", i];
        }
        
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:name]];
        imageView.frame = CGRectMake(i * w, 0, w, h);
        [self.scrollView addSubview:imageView];
    }

    // 设置默认显示的页面(实际第一页)
    [self.scrollView setContentOffset:CGPointMake(w, 0) animated:NO];
    [self.view addSubview:self.scrollView];
}

- (void)setupPageControl {
    self.pageControl = [[UIPageControl alloc] init];
    self.pageControl.frame = CGRectMake(0, CGRectGetMaxY(self.scrollView.frame) - 20, CGRectGetWidth(self.scrollView.frame), 20);
    self.pageControl.numberOfPages = 5;
    self.pageControl.currentPage = 0;
    self.pageControl.pageIndicatorTintColor = [UIColor redColor];
    self.pageControl.currentPageIndicatorTintColor = [UIColor blueColor];
    self.pageControl.userInteractionEnabled = NO;
    
    [self.view addSubview:self.pageControl];
}

- (void)setupTimer {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
}

- (void)nextPage {
    NSInteger page = self.pageControl.currentPage;
    if (page == self.pageControl.numberOfPages - 1) {
        page = 0;
    } else {
        page++;
    }
    
    CGFloat offsetX = (page + 1) * self.scrollView.frame.size.width; // 偏移量加1,因为第1页是假的
    [self.scrollView setContentOffset:CGPointMake(offsetX, 0) animated:YES];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat offsetX = scrollView.contentOffset.x;
    CGFloat pageWidth = scrollView.frame.size.width;
    
    if (offsetX >= pageWidth * 6) {
        // 滚动到假的最后一页,瞬间跳到实际第一页
        [self.scrollView setContentOffset:CGPointMake(pageWidth, 0) animated:NO];
    } else if (offsetX <= 0) {
        // 滚动到假的第一页,瞬间跳到实际最后一页
        [self.scrollView setContentOffset:CGPointMake(pageWidth * 5, 0) animated:NO];
    }

    // 更新UIPageControl的当前页
    NSInteger currentPage = (scrollView.contentOffset.x + pageWidth / 2) / pageWidth;
    if (currentPage == 0) {
        self.pageControl.currentPage = 4;
    } else if (currentPage == 6) {
        self.pageControl.currentPage = 0;
    } else {
        self.pageControl.currentPage = currentPage - 1;
    }- (void)nextPage {
    NSInteger page = self.pageControl.currentPage;
    if (page == self.pageControl.numberOfPages - 1) {
        page = 0;
    } else {
        page++;
    }
    
    CGFloat offsetX = (page + 1) * self.scrollView.frame.size.width; // 偏移量加1,因为第1页是假的
    [self.scrollView setContentOffset:CGPointMake(offsetX, 0) animated:YES];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat offsetX = scrollView.contentOffset.x;
    CGFloat pageWidth = scrollView.frame.size.width;
    
    if (offsetX >= pageWidth * 6) {
        // 滚动到假的最后一页,瞬间跳到实际第一页
        [self.scrollView setContentOffset:CGPointMake(pageWidth, 0) animated:NO];
    } else if (offsetX <= 0) {
        // 滚动到假的第一页,瞬间跳到实际最后一页
        [self.scrollView setContentOffset:CGPointMake(pageWidth * 5, 0) animated:NO];
    }
    
    // 更新UIPageControl的当前页
    NSInteger currentPage = (scrollView.contentOffset.x + pageWidth / 2) / pageWidth;
    if (currentPage == 0) {
        self.pageControl.currentPage = 4;
    } else if (currentPage == 6) {
        self.pageControl.currentPage = 0;
    } else {
        self.pageControl.currentPage = currentPage - 1;
    }
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    if ([_timer isValid]) {
        [_timer invalidate];
        _timer = nil;
    }
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    if (![_timer isValid]) {
        _timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
    }
}
@end

完整展示如下:

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/732161.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机组成原理笔记-第1章 计算机系统概论

第一章 计算机系统概论 笔记PDF版本已上传至Github个人仓库&#xff1a;CourseNotes&#xff0c;欢迎fork和star&#xff0c;拥抱开源&#xff0c;一起完善。 1.1 计算机系统简介 计算机系统组成&#xff1a; 硬件&#xff1a;计算机的实体&#xff0c;如主机、外设等软件&am…

ffmpeg windows系统详细教程

视频做预览时黑屏&#xff0c;但有声音问题解决方案。 需要将 .mp4编成H.264格式的.mp4 一般上传视频的站点&#xff0c;如YouTube、Vimeo 等&#xff0c;通常会在用户上传视频时自动对视频进行转码&#xff0c;以确保视频能够在各种设备和网络条件下流畅播放。这些网站通常…

媒体邀约有啥要注意的

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体宣传加速季&#xff0c;100万补贴享不停&#xff0c;一手媒体资源&#xff0c;全国100城线下落地执行。详情请联系胡老师。 媒体邀约是邀请媒体参与活动或报道的重要过程&#xff0c…

【国际化I18n使用方法】vue2使用i18简单实现多语种切换,刷新保持,动态数据处理

效果图 使用流程 总结就是&#xff0c;安装好插件后&#xff0c;配置几个语言的js文件&#xff0c;每个词都要在每个js内写一遍对应的语言&#xff0c;然后通过切换js文件拿到对应的语言&#xff0c;实现翻译的效果。然后当前使用什么语言保存到本地&#xff0c;这样刷新就可以…

ES数值类型慢查询优化

现象 某个查询ES接口慢调用告警&#xff0c;如图&#xff0c;接口P999的耗时都在2500ms: 基本耗时都在查询ES阶段&#xff1a; 场景与ES设定 慢调用接口为输入多个条件分页查询&#xff0c;慢调用接口调用的ES索引为 express_order_info&#xff0c;该索引通过DTS(数据同步…

搜索引擎推广基本概念与方法分享-华媒舍

销量是每个企业及个人在商业领域中追求的目标之一。而引擎霸屏推广就是一种高效的手段&#xff0c;通过该方法可以助你实现销量的狂揽。本文将为你科普引擎霸屏推广的基本概念与方法&#xff0c;帮助你了解如何运用这一有效的推广策略。 一、引擎霸屏推广 引擎霸屏推广指的是在…

【unity笔记】四、Enviro- Sky and Weather插件使用

一、 简介 Enviro内置 RP、URP、HDRP&#xff0c;开箱即用。 动态天空 随附的天空系统经过精心设计&#xff0c;以实现最佳性能和灵活性。使用多种颜色渐变&#xff0c;而不是调整人工数字。为您的项目创建独特且非常逼真的天空非常简单&#xff01; 灯光 由 Enviro 控制的逼…

Redis大key有什么危害?如何排查和处理?

什么是 bigkey&#xff1f; 简单来说&#xff0c;如果一个 key 对应的 value 所占用的内存比较大&#xff0c;那这个 key 就可以看作是 bigkey。具体多大才算大呢&#xff1f;有一个不是特别精确的参考标准&#xff1a; String 类型的 value 超过 1MB 复合类型&#xff08;Li…

WPF 程序 分布式 自动更新 登录 打包

服务器server端 core api 客户端WPF // 检查应用更新 //1、获取最新文件列表 // var files fileService.GetUpgradeFiles(); // 2、文件判断&#xff0c;新增的直接下载&#xff1b;更新的直接下载&#xff1b;删除的直接删除 // 客户端本地需要一个记录…

CentOS7在2024.6.30停止维护后,可替代的Linux操作系统

背景 Linux的发行版本可以大体分为两类&#xff0c;一类是商业公司维护的发行版本&#xff0c;一类是社区组织维护的发行版本&#xff0c;前者以著名的Redhat&#xff08;RHEL&#xff09;为代表&#xff0c;后者以Debian为代表。国内占有率最多的却是Centos&#xff0c;这是由…

C++设计模式——Composite组合模式

一&#xff0c;组合模式简介 真实世界中&#xff0c;像企业组织、文档、图形软件界面等案例&#xff0c;它们在结构上都是分层次的。将系统分层次的方式使得统一管理和添加不同子模块变得容易&#xff0c;在软件开发中&#xff0c;组合模式的设计思想和它们类似。 组合模式是…

Navicat和SQLynx产品功能比较二(SQL查询)

数据库管理工具最常用的功能就是SQL的查询&#xff0c;没有之一。本文针对Navicat和SQLynx做了SQL查询相关的性能测试&#xff0c;从测试结果来看&#xff0c;Navicat主要适合开发类的小型数据量需求&#xff0c;SQLynx可以适应大型数据量或小型数据量的需求&#xff0c;用户可…

自制HTML5游戏《开心消消乐》

1. 引言 游戏介绍 《开心消消乐》是一款基于HTML5技术开发的网页游戏&#xff0c;以其简单的操作方式、轻松的游戏体验和高度的互动性&#xff0c;迅速在社交平台上获得了广泛的关注和传播。玩家通过消除相同类型的元素来获得分数&#xff0c;游戏设计巧妙&#xff0c;易于上手…

Ubuntu系统使用快速入门实践(八)—— git 命令使用

Ubuntu系统使用快速入门实践系列文章 下面是Ubuntu系统使用系列文章的总链接&#xff0c;本人发表这个系列的文章链接均收录于此 Ubuntu系统使用快速入门实践系列文章总链接 下面是专栏地址&#xff1a; Ubuntu系统使用快速入门实践系列文章专栏 文章目录 Ubuntu系统使用快速…

锂锗磷硫(LGPS)是代表性硫化物固态电解质产品之一 技术研究不断深入

锂锗磷硫&#xff08;LGPS&#xff09;是代表性硫化物固态电解质产品之一 技术研究不断深入 锂锗磷硫&#xff0c;化学式为Li10GeP2S12&#xff0c;英文简称LGPS&#xff0c;是一种代表性硫化物固态电解质&#xff0c;外观为灰白色至深灰色固体粉末状。 全固态锂电池是传统液态…

网上的流量卡真的可以免费领取吗?

网上的流量卡真的可以免费领取吗&#xff1f;当然可以&#xff0c;目前运营商推出的流量卡都是可以免费领取的。 有很多朋友私信给小编&#xff0c;听说流量卡是免费领取的就觉得不太靠谱&#xff0c;其实这种想法是不对的&#xff0c;首先大家要换位思考一下&#xff0c;如果我…

[JS]数据类型

介绍 在计算中一切事物都是数据, 为了提高数据的存储和使用效率, 要对数据进行类型的分类 栈(操作系统): 由操作系统自动分配释放函数的参数值, 局部变量的值等, 其操作方式类似于数据结构中的栈; 基本数据类型存放在栈里面string, number, boolean, undefined, null 堆(操作…

设计模式1-简介

设计模式简介 专栏的目的什么是设计模式设计模式要学什么软件开发原则主流设计模式 学习建议经典面试题 资源 专栏的目的 1.理解松耦合的设计思想 2.掌握面向对象设计原则 3.掌握重构技法改善设计 4.掌握GOF核心设计模式 什么是设计模式 每一个模式描述了一个在我们周围不…

docker 环境部署

1.Redis部署 用docker拉取redis镜像 docker pull redis 用docker查看拉取的镜像版本号&#xff0c;这里查到的是 6.2.6 版本 docker inspect redis 通过wget指令下载对应版本的tar包&#xff0c;下载完成后解压 wget https://download.redis.io/releases/redis-6.2.6.tar.gz …

【2024.6.22】今日科技时事:科技前沿大事件

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…