• 从对轮播图的封装中体会 面对接口 编程的思想


    .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这样的辅助来标记函数及作用, 这样可以很快找到要找的代码.

    好开心, 写完了.*.*^.^  ^.^

  • 相关阅读:
    Backbone源码解析(六):观察者模式应用
    NodeJs 开发微信公众号(五)真实环境部署
    NodeJs 开发微信公众号(四)微信网页授权
    NodeJs 开发微信公众号(三)微信事件交互
    NodeJs 开发微信公众号(二)测试环境部署
    NodeJs 开发微信公众号(一)准备工作
    Css 动画的回调
    GIT常用命令笔记
    论如何在手机端web前端实现自定义原生控件的样式
    Box-sizing:小身材,大拳头!
  • 原文地址:https://www.cnblogs.com/mafeng/p/5698915.html
Copyright © 2020-2023  润新知