iOS开发基础8-UIScrollView

在 iOS 开发中,UIScrollView 是一个非常重要的滚动视图控件。通过掌握其基本属性、代理方法及高级应用如图片轮播器,我们可以创建出表现力丰富、用户体验良好的应用界面。本文将详细介绍 UIScrollView 的基础用法、属性配置、事件监听、图片缩放,以及如何实现图片轮播器,并进行底层逻辑的分析。

一、UIScrollView 的基本用法

使用步骤

  1. 创建 UIScrollView。
  2. 将要展示的内容添加到 UIScrollView。
  3. 设置 UIScrollView 的滚动范围 (contentSize)。

示例代码

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 添加子控件到 UIScrollView
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
    [self.scrollView addSubview:btn];
    
    UISwitch *sw = [[UISwitch alloc] init];
    CGRect tempFrame = sw.frame;
    tempFrame.origin.y = 150;
    sw.frame = tempFrame;
    [self.scrollView addSubview:sw];
    
    UIButton *customBtn = [[UIButton alloc] init];
    customBtn.frame = CGRectMake(0, 0, 100, 100);
    customBtn.backgroundColor = [UIColor redColor];
    [customBtn setTitle:@"我是按钮" forState:UIControlStateNormal];
    [self.scrollView addSubview:customBtn];
    
    // 设置滚动范围
    self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width + 100, self.scrollView.frame.size.height + 100);
}

底层逻辑分析

  1. 滚动范围contentSize 决定了 UIScrollView 可以滚动的范围。如果不设置,默认不能滚动。
  2. 自动布局:UIScrollView 可以根据添加的子控件自动调整其内容尺寸,但需要手动调用 contentSize 进行微调。

二、UIScrollView 的基本属性

常见属性

  1. scrollEnabled:控制是否允许滚动,默认值是 YES
  2. userInteractionEnabled:控制是否允许用户交互,比如点击操作,默认是 YES
  3. showsHorizontalScrollIndicatorshowsVerticalScrollIndicator:是否显示水平或垂直滚动条。

示例

self.scrollView.scrollEnabled = NO;
self.scrollView.userInteractionEnabled = NO;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
self.scrollView.bounces = YES;

// 设置是否可以回弹
self.scrollView.alwaysBounceVertical = YES;
self.scrollView.alwaysBounceHorizontal = YES;

底层逻辑分析

  1. 控制滚动:通过 scrollEnabled 属性可以灵活控制 UIScrollView 是否需要响应滚动操作。
  2. 用户交互userInteractionEnabled 能关闭一切用户交互,但不会影响内部逻辑。
  3. 滚动条与回弹:滚动条和回弹效果提升了用户体验,尤其在内容较多时。

三、 UIScrollView 事件监听

事件监听步骤

  1. 成为 UIScrollView 的代理。
  2. 遵守并实现 UIScrollView 的协议方法。

示例代码

@interface ViewController () <UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *sc;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    self.sc.delegate = self;
}

#pragma mark - UIScrollViewDelegate

// 滚动时调用
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    NSLog(@"%s", __func__);
}

// 开始拖拽时调用
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    NSLog(@"%s", __func__);
}

// 停止拖拽时调用
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    NSLog(@"%s", __func__);
    if (!decelerate) {
        [self scrollViewDidEndDecelerating:scrollView];
    }
}

// 停止减速时调用
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    NSLog(@"UIScrollView停止滚动了");
}

底层逻辑分析

  1. 代理机制:通过代理机制,控制器可以监听 UIScrollView 的一切变化,提供完全的控制权。
  2. 弱引用代理:防止循环引用,避免内存泄漏。
  3. 多样化回调:根据滚动状态不同,提供了多种回调方法,使得开发者可以在不同的时间点执行相应操作。

四、 缩放图片

配置缩放

  1. 设定最大和最小缩放比例。
  2. 通过代理方法告诉 UIScrollView 哪个控件需要缩放。

示例代码

self.sc.maximumZoomScale = 2.0;
self.sc.minimumZoomScale = 0.5;
self.sc.delegate = self;

#pragma mark - UIScrollViewDelegate

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return self.iv;
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    NSLog(@"%s", __func__);
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
    NSLog(@"%s", __func__);
}

底层逻辑分析

  1. 缩放策略:通过最大和最小缩放比例控制缩放范围。
  2. 指定缩放对象:代理方法明确指定需要缩放的子控件,避免混乱。
  3. 缩放回调:在缩放过程中提供持续的反馈,便于响应用户操作。

五、图片轮播器

通过 UIScrollView 实现图片轮播器是一个常见的需求。以下是实现过程:

基本思路

  1. 创建 UIScrollView 并添加子控件 UIImageView。
  2. 通过定时器实现图片的自动滚动。
  3. 添加 UIPageControl 以指示当前页码。

示例代码

XMGPageView 头文件

#import <UIKit/UIKit.h>

@interface XMGPageView : UIView

+ (instancetype)pageView;

@property (nonatomic, strong) NSArray *imageNames;

@end

XMGPageView 实现

#import "XMGPageView.h"

@interface XMGPageView () <UIScrollViewDelegate>

@property (weak, nonatomic) IBOutlet UIScrollView *sc;
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl;
@property (weak, nonatomic) NSTimer *timer;

@end

@implementation XMGPageView

+ (instancetype)pageView {
    return [[[NSBundle mainBundle] loadNibNamed:@"XMGPageView" owner:nil options:nil] lastObject];
}

- (void)awakeFromNib {
    [super awakeFromNib];
    
    self.sc.delegate = self;
    self.sc.showsHorizontalScrollIndicator = NO;
    self.sc.showsVerticalScrollIndicator = NO;
    self.sc.bounces = NO;
    self.sc.pagingEnabled = YES;
    
    [self.pageControl addTarget:self action:@selector(pageControlClick:) forControlEvents:UIControlEventValueChanged];
    
    [self.pageControl setValue:[UIImage imageNamed:@"current"] forKeyPath:@"_currentPageImage"];
    [self.pageControl setValue:[UIImage imageNamed:@"other"] forKeyPath:@"_pageImage"];
    
    [self startTimer];
}

- (IBAction)pageControlClick:(UIPageControl *)sender {
    self.sc.contentOffset = CGPointMake(sender.currentPage * self.sc.frame.size.width, 0);
}

- (void)startTimer {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage:) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)nextPage:(NSTimer *)timer {
    NSUInteger page = self.pageControl.currentPage + 1;
    if (page >= self.imageNames.count) {
        self.pageControl.currentPage = 0;
    } else {
        self.pageControl.currentPage = page;
    }
    [self pageControlClick:self.pageControl];
}

- (void)stopTimer {
    [self.timer invalidate];
}

- (void)setImageNames:(NSArray *)imageNames {
    _imageNames = imageNames;
    
    for (UIView *subView in self.sc.subviews) {
        [subView removeFromSuperview];
    }
    
    for (int i = 0; i < _imageNames.count; i++) {
        UIImageView *iv = [[UIImageView alloc] init];
        iv.image = [UIImage imageNamed:_imageNames[i]];
        [self.sc addSubview:iv];
    }
    
    self.pageControl.numberOfPages = _imageNames.count;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    CGFloat width = self.sc.frame.size.width;
    CGFloat height = self.sc.frame.size.height;
    NSUInteger imageCount = self.imageNames.count;
    
    for (int i = 0; i < imageCount; i++) {
        UIImageView *iv = self.sc.subviews[i];
        iv.frame = CGRectMake(i * width, 0, width, height);
    }
    
    self.sc.contentSize = CGSizeMake(imageCount * width, height);
}

#pragma mark - UIScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat page = scrollView.contentOffset.x / scrollView.frame.size.width;
    int currentPage = page + 0.5;
    self.pageControl.currentPage = currentPage;
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [self stopTimer];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    [self startTimer];
}

@end

底层逻辑分析

  1. 定时器控制:通过定时器实现自动翻页,并在用户操作时暂停和重启定时器。
  2. 分页控制:UIPageControl 与 UIScrollView 结合,实现手动和自动分页的同步展示。
  3. 界面更新:通过 set 方法和 layoutSubviews 方法确保界面的一致性和适应性。

结论

通过深入理解 UIScrollView 的基本用法、事件监听、图片缩放以及实现图片轮播器,我们可以创建出流畅且具有丰富交互的应用界面。这些技术和技巧在实际项目中非常实用,可以大大提升用户体验和开发效率。

posted @   Mr.陳  阅读(1033)  评论(0编辑  收藏  举报
编辑推荐:
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示