.h 文件的内容:
#import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @interface MFCyclmage : UIView // 私有属性, 可以对外公开, 使用readonly // 一般让别人去修改这个属性的属性(不能修改这个属性) @property (nonatomic, strong, readonly) UIScrollView *cycleScrollView; @property (nonatomic, strong, readonly) UIPageControl *imagePage; // 初始化 // 相关于图片的数组 // 第一种赋值方式, 通过setter方法 @property (nonatomic, strong) NSArray *imagesArray; // 第二种赋值方式, 自定义方法 - (void)setImageWithArray:(nonnull NSArray *)array; @end NS_ASSUME_NONNULL_END
.m 文件的内容:
#import "MFCyclmage.h" @interface MFCyclmage ()<UIScrollViewDelegate> @property (nonatomic, strong) UIScrollView *cycleScrollView; @property (nonatomic, strong) UIPageControl *imagePage; @end @implementation MFCyclmage #pragma mark -- scrollView Delegate - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { // 改变pageController的对应下标 NSInteger page = scrollView.contentOffset.x / self.bounds.size.width; _imagePage.currentPage = page - 1; if (page == 0) { // 改变滚动视图的下标 _cycleScrollView.contentOffset = CGPointMake(self.bounds.size.width * (_imagesArray.count - 2), 0); _imagePage.currentPage = _imagePage.numberOfPages - 1; } else if (page == _imagesArray.count - 1) { _cycleScrollView.contentOffset = CGPointMake(self.bounds.size.width, 0); _imagePage.currentPage = 0; } } #pragma mark - initWithFrame - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%% // 子控件初始化时, frame不要与初始化方法的frame一样 // 推荐使用self.bounds / CGRectMake(0, 0, frame.size.width, self.size.height) //%%%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ _cycleScrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; [self addSubview:_cycleScrollView]; [self initCycleScrollView]; [self createImagePage:frame]; } return self; } - (void)initCycleScrollView { _cycleScrollView.backgroundColor = [UIColor blackColor]; _cycleScrollView.bounces = NO; _cycleScrollView.pagingEnabled = YES; _cycleScrollView.showsHorizontalScrollIndicator = NO; _cycleScrollView.delegate = self; } - (void)createImagePage:(CGRect)frame { _imagePage = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 0, frame.size.width / 3, 20)]; [self addSubview:_imagePage]; _imagePage.numberOfPages = 5; _imagePage.center = CGPointMake(frame.size.width / 2, frame.size.height - 20 / 2); [_imagePage addTarget:self action:@selector(imagePageChange:) forControlEvents:UIControlEventValueChanged]; } - (void)imagePageChange:(UIPageControl *)page { [_cycleScrollView setContentOffset:CGPointMake((page.currentPage + 1) * self.bounds.size.width, 0) animated:YES]; } #pragma mark -- ImageArray - (void)setImageWithArray:(NSArray *)array { // 根据传过来的数组, 进行处理, 生成图片数组 NSArray *imageArray = [self handleArray:array]; // 逻辑判断时, 用到的数组内容 self.imagesArray = [NSArray arrayWithArray:imageArray]; // 根据图片数组, 生成对应得imageView [self createAllImagesWithImageArray:imageArray]; // 根据图片数组生成对应的视图 _imagePage.numberOfPages = array.count; _cycleScrollView.contentSize = CGSizeMake(imageArray.count * self.bounds.size.width, 0); // 因为添加了最后一张图, 要让轮播图看起来在第一张 _cycleScrollView.contentOffset = CGPointMake(self.bounds.size.width, 0); } - (NSArray *)handleArray:(NSArray *)array { // 判断array元素的数据类型(不知道别人使用你的类的时候传进来的类型是什么) id firstObject = [array firstObject]; // 最终的图片数组 NSMutableArray *imageArray = [NSMutableArray array]; // 判断对象是不是某种类型 isKindOfClass if ([firstObject isKindOfClass:[NSString class]]) { // 把 字符串 数组 转成 图片 数组 for (NSString *imageName in array) { UIImage *image = [UIImage imageNamed:imageName]; [imageArray addObject:image]; } } else if ([firstObject isKindOfClass:[UIImage class]]) { // 这个方法用不可变数组将自己转为可变数组 [imageArray setArray:array]; } // 获取到第一张图 UIImage *firstimage = [imageArray firstObject]; // 获取到最后一张图 UIImage *lastImage = [imageArray lastObject]; // 下标为0的位置放最后一张图 [imageArray insertObject:lastImage atIndex:0]; // 在最后添加一张图 [imageArray addObject:firstimage]; // 用copy可以直接将可变的数组变为不可变的, 类比mutablecopy return [imageArray copy]; } - (void)createAllImagesWithImageArray:(NSArray *)imageArray { // 使用数组创建imageView for (int i = 0; i < imageArray.count; i++) { UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(i * self.bounds.size.width, 0, self.bounds.size.width, self.bounds.size.height)]; imageView.image = imageArray[i]; [_cycleScrollView addSubview:imageView]; } } @end
调用的代码:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. MFCyclmage *cycle = [[MFCyclmage alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 200)]; NSArray *array = @[@"0", @"1", @"2", @"3", @"4"]; [cycle setImageWithArray:array]; // 这里是改变readonly属性的属性 cycle.cycleScrollView.showsHorizontalScrollIndicator = YES; [self.view addSubview:cycle]; }
运行的效果图:
总结一下:
面向接口封装时,要注意以下几点:
1. 首先要考虑.h文件中写那些属性, 用户要用到哪些方法,属性. 如果是私有属性的话, 可以readonly来修饰, 写到.h文件中.这样, 用户就可以改变这个属性的属性;
2. 要用到iOS9的新特性, 用nullable, 或nonnull 来修饰参数, 这样用户使用起来更方便;
3. 在.m文件中, 尽量简化初始化函数中的内容, 用调用函数方法来替代;
4. .m文件中的函数方法, 不是随便想些什么函数就写什么函数, 而是将功能一样的代码用函数封装起来, 这样哪一部分出问题既可以很快的找到;
5. 用形如#pragma mark -- scrollView Delegate这样的辅助来标记函数及作用, 这样可以很快找到要找的代码.
好开心, 写完了.*.*^.^ ^.^