ios学习开源代码系列(一)LHLoadingView

模仿是学习的必经阶段,所以接下来决定学习开源的代码,来发现自己的不足,学习别人的优点

第一个学习的框架是LHLoadingView开源代码

首先看看项目的代码结构


项目结构很简单,主要包活

1.自定义的view,view里面实现了动画效果

2.view的controller


首先看看controller,里面主要做的使view定时执行动画,代码就只有几行,不复杂



然后我们看看自定义的view,view只有一个 show 方法

@interface LeoLoadingView : UIView

-(void)showView:(BOOL)show;

@end

下面贴上实现代码

#import "LeoLoadingView.h"

#define AnimationTime   0.3

#define kDotSize                (CGSizeMake(0.4 * self.frame.size.width, 0.4 * self.frame.size.height))

#define LeftTopPosition         (CGPointMake(0, 0))
#define LeftTBottomPosition     (CGPointMake(0, 0.6 * self.frame.size.height))
#define RightBottomPosition     (CGPointMake(0.6 * self.frame.size.width, 0.6 * self.frame.size.height))
#define RightTopPosition        (CGPointMake(0.6 * self.frame.size.width, 0))

#define kDotColor               [UIColor colorWithRed:200/255.0 green:206/255.0 blue:221/255.0 alpha:1.0]

@interface LeoLoadingView()

@property (strong, nonatomic) UIView *dotView0,*dotView1,*dotView2;
@property (strong, nonatomic) NSArray *dotViews;
@property (assign, nonatomic) NSInteger dotIndex;
@property (strong, nonatomic) NSTimer *timer;
@end

@implementation LeoLoadingView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.hidden = YES;
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    [self initView];
}

- (void)initView
{
    self.backgroundColor = [UIColor clearColor];
//    self.clipsToBounds = YES;
    self.userInteractionEnabled = NO;
    
    _dotView0 = [[UIView alloc]initWithFrame:(CGRect){RightBottomPosition, kDotSize}];
    _dotView0.backgroundColor = kDotColor;
    [self addSubview:_dotView0];
    
    _dotView1 = [[UIView alloc]initWithFrame:(CGRect){LeftTBottomPosition, kDotSize}];
    _dotView1.backgroundColor = kDotColor;
    [self addSubview:_dotView1];
    
    _dotView2 = [[UIView alloc]initWithFrame:(CGRect){LeftTopPosition, kDotSize}];
    _dotView2.backgroundColor = kDotColor;
    [self addSubview:_dotView2];
    
    _dotViews = @[_dotView0, _dotView1, _dotView2];
    _dotIndex = 0;
}

-(void)showView:(BOOL)show
{
    if (show) {
        self.hidden = NO;
        if (!_timer) {
            _timer = [NSTimer timerWithTimeInterval:AnimationTime target:self selector:@selector(beginAnimation) userInfo:nil repeats:YES];
            [[NSRunLoop mainRunLoop]addTimer:_timer forMode:NSRunLoopCommonModes];
        }
    }
    else{
        [_timer invalidate];
        _timer = nil;
        self.hidden = YES;
    }
}

-(void)beginAnimation
{
    [UIView animateWithDuration:AnimationTime delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        UIView *dotView = _dotViews[_dotIndex];
        [self moveDotViewToNextPosition:dotView];
        
        _dotIndex ++;
        _dotIndex = _dotIndex > 2 ? 0 : _dotIndex;
        
    } completion:nil];
}

-(void)moveDotViewToNextPosition:(UIView*)dotView
{
    if (CGPointEqualToPoint(dotView.frame.origin, LeftTopPosition)) {
        dotView.frame = (CGRect){LeftTBottomPosition, dotView.frame.size};
    }
    else if (CGPointEqualToPoint(dotView.frame.origin, LeftTBottomPosition)) {
        dotView.frame = (CGRect){RightBottomPosition, dotView.frame.size};
    }
    else if (CGPointEqualToPoint(dotView.frame.origin, RightBottomPosition)) {
        dotView.frame = (CGRect){RightTopPosition, dotView.frame.size};
    }
    else if (CGPointEqualToPoint(dotView.frame.origin, RightTopPosition)) {
        dotView.frame = (CGRect){LeftTopPosition, dotView.frame.size};
    }
}

@end


那么多个方法,我们首先要关注的,也是很重要的一个方法就是

- (void)layoutSubviews
{
    [super layoutSubviews];
    [self initView];
}

当自定义view首次加载到父view中时(或者当view是rootview时,屏幕发生旋转;或者view的frame发生改变;又或往view添加subview时),会调用这个方法,通过initView来初始化自定义view。这里有一个有趣的问题,大家猜猜当这个工程启动的时候,layoutSubviews会被调用多少次?通过打断点,我们可以发现是两次。为什么呢?那是因为我们在storyboard里面添加了两个自定义的view,既然有两个view,那么当这两个view添加进父view的时候,自然就会调用两次了~


第二个要关注的方法是

-(void)showView:(BOOL)show
{
    if (show) {
        self.hidden = NO;
        if (!_timer) {
            _timer = [NSTimer timerWithTimeInterval:AnimationTime target:self selector:@selector(beginAnimation) userInfo:nil repeats:YES];
            [[NSRunLoop mainRunLoop]addTimer:_timer forMode:NSRunLoopCommonModes];
        }
    }
    else{
        [_timer invalidate];
        _timer = nil;
        self.hidden = YES;
    }
}

NSRunLoop是平时比较少接触的概念,下面说说自己查阅资料后的理解

NSRunLoop和线程是分不开的,ios里面每一个线程(包活自己新建的和应用的mian线程)都有一个NSRunLoop对象

一般来说,一个线程当执行一段代码后,就会退出,那如果我想在线程里面循环执行某项任务,那怎么办呢,我们可能会这样写

void threadMethod()
{
    while(true)
    {
        do some job...
        sleep(一段时间)
    }
}

而ios也提供了类似这样的机制,这就是NSRunLoop了,当NSRunLoop运行起来后,他就会类似上面那段代码那样工作,当有数据源或timer事件向它发送消息的时候,线程就会被唤醒工作,调用我们注册的handler(函数)去执行我们自定义的任务。就像上面showView方法一样,方法里面新建了一个timer,timer会定时地在UIView上执行动画,然后把timer注册到主线程(主线程一般负载界面的更新和重绘)的NSRunLoop里面,所以每隔一定时间时,timer就能在主线程上执行动画

最后就是通过UIView来实现动画的绘制

-(void)beginAnimation
{
    [UIView animateWithDuration:AnimationTime delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        UIView *dotView = _dotViews[_dotIndex];
        [self moveDotViewToNextPosition:dotView];
        
        _dotIndex ++;
        _dotIndex = _dotIndex > 2 ? 0 : _dotIndex;
        
    } completion:nil];
}

-(void)moveDotViewToNextPosition:(UIView*)dotView
{
    if (CGPointEqualToPoint(dotView.frame.origin, LeftTopPosition)) {
        dotView.frame = (CGRect){LeftTBottomPosition, dotView.frame.size};
    }
    else if (CGPointEqualToPoint(dotView.frame.origin, LeftTBottomPosition)) {
        dotView.frame = (CGRect){RightBottomPosition, dotView.frame.size};
    }
    else if (CGPointEqualToPoint(dotView.frame.origin, RightBottomPosition)) {
        dotView.frame = (CGRect){RightTopPosition, dotView.frame.size};
    }
    else if (CGPointEqualToPoint(dotView.frame.origin, RightTopPosition)) {
        dotView.frame = (CGRect){LeftTopPosition, dotView.frame.size};
    }
}



代码就分析完了,那总得练一下手,于是仿照上面的实现,我自己也些了一段小小的代码,实现的是一个指示器的功能,三个正方形轮流从左到右放大然后缩小



贴上完整的代码

#import "MyOwnView.h"

#define AnimationTime 0.3
#define TimerTime 0.5

#define ViewSize (CGSizeMake(20, 20))
#define View1Position (CGPointMake((self.frame.size.width / 4.0) * 1, 20))
#define View2Position (CGPointMake((self.frame.size.width / 4.0) * 2, 20))
#define View3Position (CGPointMake((self.frame.size.width / 4.0) * 3, 20))

@interface MyOwnView()
@property(nonatomic, strong) UIView* view1;
@property(nonatomic, strong) UIView* view2;
@property(nonatomic, strong) UIView* view3;
@property(nonatomic, strong) NSArray *views;
@property(nonatomic, strong) NSTimer* timer;
@property(nonatomic, assign) int count;
-(void)doAnimation;
@end

@implementation MyOwnView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self initViews];
    }
    return self;
}

- (void)initViews
{
    _view1 = [[UIView alloc]initWithFrame:(CGRect){View1Position, ViewSize}];
    _view1.backgroundColor = [UIColor greenColor];
    [self addSubview:_view1];
    _view2 = [[UIView alloc]initWithFrame:(CGRect){View2Position, ViewSize}];
    _view2.backgroundColor = [UIColor greenColor];
    [self addSubview:_view2];
    _view3 = [[UIView alloc]initWithFrame:(CGRect){View3Position, ViewSize}];
    _view3.backgroundColor = [UIColor greenColor];
    [self addSubview:_view3];
    _views = @[_view1, _view2, _view3];
}

- (void)drawRect:(CGRect)rect
{
    NSLog(@"drawRect");
}

-(void)layoutSubviews
{
//    [self initViews];
    NSLog(@"layoutSubviews");
}

-(void)showView:(BOOL)show
{
    if(show) {
        _count = 0;
        _timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(doAnimation) userInfo:nil repeats:YES];
        [[NSRunLoop mainRunLoop]addTimer:_timer forMode:NSDefaultRunLoopMode];
    }else {
        [_timer invalidate];
    }
}

-(void)doAnimation
{
    UIView *view = [_views objectAtIndex:_count];
    CGAffineTransform t = view.transform;
    
    [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionAutoreverse animations:^{
        [view setTransform:CGAffineTransformScale(view.transform, 1.5, 1.5)];
    } completion:^(BOOL finish){
        [view setTransform:t];
    }];
    
    _count++;
    _count = _count > 2 ? 0 : _count;
}
@end


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值