iOS学习——UIPickerView的实现年月选择器

  最近项目上需要用到一个选择器,选择器中的内容只有年和月,而在iOS系统自带的日期选择器UIDatePicker中却只有四个选项如下,分别是时间(时分秒)、日期(年月日)、日期+时间(年月日时分)以及倒计时。其中并没有我们所需要的只显示年月的选择器,在网上找了很多相关的资料,但是觉得都写得过于麻烦。因此,为了满足项目需求,自己用UIPickerView写了一个只显示年月的选择器界面,同时还可以控制我们的显示的最小时间。当然,如果要控制其他内容也都是可以的,无非就是在数据处理上多一些处理和控制。

typedef NS_ENUM(NSInteger, UIDatePickerMode) {
    UIDatePickerModeTime,           // Displays hour, minute, and optionally AM/PM designation depending on the locale setting (e.g. 6 | 53 | PM)
    UIDatePickerModeDate,           // Displays month, day, and year depending on the locale setting (e.g. November | 15 | 2007)
    UIDatePickerModeDateAndTime,    // Displays date, hour, minute, and optionally AM/PM designation depending on the locale setting (e.g. Wed Nov 15 | 6 | 53 | PM)
    UIDatePickerModeCountDownTimer, // Displays hour and minute (e.g. 1 | 53)
} __TVOS_PROHIBITED;

一 整体方案 

  在整个实现中分为两个部分,首先是用一个基类来布局我们选择器的整体布局,包括我们的选择器的标题,取消、确定按钮,蒙层等大框架的布局,然后是子类在基类的基础上添加UIPickerView来实现选择器的基本功能以及数据加载和显示。首先,我们来看一下整体的一个效果,点击某个设定的控件,然后弹出下图所示的一个选择器,选择器的选项主要就是显年月的信息:

二 基类布局

  在上一部分说了,基类布局主要是对整体的架构进行布局,我们先看下有哪些内容,包括了背景蒙层视图、弹出视图(包含标题行(又包含取消按钮、确定按钮和标题)、分割线和选择器),在子类中会进行一个整体的布局,在 - (void)initUI 方法中进行布局。

//
//  BaseView.h

#import <UIKit/UIKit.h>

#define kDatePicHeight 200  //选择器的高度
#define kTopViewHeight 44   //取消 标题 确定 行高度

#define SCREEN_BOUNDS [UIScreen mainScreen].bounds
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
@interface BaseView : UIView
// 背景蒙层视图
@property (nonatomic, strong) UIView *backgroundView;
// 弹出视图
@property (nonatomic, strong) UIView *alertView;
// 标题行顶部视图
@property (nonatomic, strong) UIView *topView;
// 左边取消按钮
@property (nonatomic, strong) UIButton *leftBtn;
// 右边确定按钮
@property (nonatomic, strong) UIButton *rightBtn;
// 中间标题
@property (nonatomic, strong) UILabel *titleLabel;
// 分割线视图
@property (nonatomic, strong) UIView *lineView;

/** 初始化子视图 ,整体布局*/
- (void)initUI;

//以下三种方法在基类中的实现都是空白的,具体的效果在子类中重写
/** 点击背景遮罩图层事件 */
- (void)didTapBackgroundView:(UITapGestureRecognizer *)sender;
/** 取消按钮的点击事件 */
- (void)clickLeftBtn;
/** 确定按钮的点击事件 */
- (void)clickRightBtn;

@end

  具体的.m文件的实现代码如下,进行折叠了,需要的可以直接拷贝,在后面我们再 进行具体分析每一步的布局和设置。

  1 //
  2 //  BaseView.m
  3 //  CJMobile
  4 //
  5 //  Created by mukekeheart on 2017/12/12.
  6 //  Copyright © 2017年 长江证券. All rights reserved.
  7 //
  8 
  9 #import "BaseView.h"
 10 
 11 @implementation BaseView
 12 
 13 - (void)initUI {
 14     self.frame = SCREEN_BOUNDS;
 15     // 背景遮罩图层
 16     [self addSubview:self.backgroundView];
 17     // 弹出视图
 18     [self addSubview:self.alertView];
 19     // 设置弹出视图子视图
 20     // 添加顶部标题栏
 21     [self.alertView addSubview:self.topView];
 22     // 添加左边取消按钮
 23     [self.topView addSubview:self.leftBtn];
 24     // 添加右边确定按钮
 25     [self.topView addSubview:self.rightBtn];
 26     // 添加中间标题按钮
 27     [self.topView addSubview:self.titleLabel];
 28     // 添加分割线
 29     [self.topView addSubview:self.lineView];
 30 }
 31 
 32 #pragma mark - 背景遮罩图层
 33 - (UIView *)backgroundView {
 34     if (!_backgroundView) {
 35         _backgroundView = [[UIView alloc]initWithFrame:SCREEN_BOUNDS];
 36         _backgroundView.backgroundColor = [UIColor blackColor] ;
 37         _backgroundView.alpha = 0.3f ;
 38         _backgroundView.userInteractionEnabled = YES;
 39         UITapGestureRecognizer *myTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(didTapBackgroundView:)];
 40         [_backgroundView addGestureRecognizer:myTap];
 41     }
 42     return _backgroundView;
 43 }
 44 
 45 #pragma mark - 弹出视图
 46 - (UIView *)alertView {
 47     if (!_alertView) {
 48         _alertView = [[UIView alloc]initWithFrame:CGRectMake(0, SCREEN_HEIGHT - kTopViewHeight - kDatePicHeight, SCREEN_WIDTH, kTopViewHeight + kDatePicHeight)];
 49         _alertView.backgroundColor = [UIColor whiteColor];
 50     }
 51     return _alertView;
 52 }
 53 
 54 #pragma mark - 顶部标题栏视图
 55 - (UIView *)topView {
 56     if (!_topView) {
 57         _topView =[[UIView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, kTopViewHeight + 0.5)];
 58         _topView.backgroundColor = [UIColor whiteColor];
 59     }
 60     return _topView;
 61 }
 62 
 63 #pragma mark - 左边取消按钮
 64 - (UIButton *)leftBtn {
 65     if (!_leftBtn) {
 66         _leftBtn = [UIButton buttonWithType:UIButtonTypeCustom];
 67         _leftBtn.frame = CGRectMake(5, 8, 60, 28);
 68         _leftBtn.backgroundColor = [UIColor clearColor];
 69         _leftBtn.layer.masksToBounds = YES;
 70         _leftBtn.titleLabel.font = [UIFont systemFontOfSize:17.0f];
 71         [_leftBtn setTitleColor:kGrayFontColor forState:UIControlStateNormal];
 72         [_leftBtn setTitle:@"取消" forState:UIControlStateNormal];
 73         [_leftBtn addTarget:self action:@selector(clickLeftBtn) forControlEvents:UIControlEventTouchUpInside];
 74     }
 75     return _leftBtn;
 76 }
 77 
 78 #pragma mark - 右边确定按钮
 79 - (UIButton *)rightBtn {
 80     if (!_rightBtn) {
 81         _rightBtn = [UIButton buttonWithType:UIButtonTypeCustom];
 82         _rightBtn.frame = CGRectMake(SCREEN_WIDTH - 65, 8, 60, 28);
 83         _rightBtn.backgroundColor = [UIColor clearColor];
 84         _rightBtn.layer.masksToBounds = YES;
 85         _rightBtn.titleLabel.font = [UIFont systemFontOfSize:17.0f];
 86         [_rightBtn setTitleColor:kBlueFontColor forState:UIControlStateNormal];
 87         [_rightBtn setTitle:@"确定" forState:UIControlStateNormal];
 88         [_rightBtn addTarget:self action:@selector(clickRightBtn) forControlEvents:UIControlEventTouchUpInside];
 89     }
 90     return _rightBtn;
 91 }
 92 
 93 #pragma mark - 中间标题按钮
 94 - (UILabel *)titleLabel {
 95     if (!_titleLabel) {
 96         _titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(65, 0, SCREEN_WIDTH - 130, kTopViewHeight)];
 97         _titleLabel.backgroundColor = [UIColor clearColor];
 98         _titleLabel.font = [UIFont systemFontOfSize:17.0f];
 99         _titleLabel.textColor = kBlackFontColor;
100         _titleLabel.textAlignment = NSTextAlignmentCenter;
101     }
102     return _titleLabel;
103 }
104 
105 #pragma mark - 分割线
106 - (UIView *)lineView {
107     if (!_lineView) {
108         _lineView = [[UIView alloc]initWithFrame:CGRectMake(0, kTopViewHeight, SCREEN_WIDTH, 0.5)];
109         _lineView.backgroundColor  = [UIColor colorWithRed:225 / 255.0 green:225 / 255.0 blue:225 / 255.0 alpha:1.0];
110         [self.alertView addSubview:_lineView];
111     }
112     return _lineView;
113 }
114 
115 #pragma mark - 点击背景遮罩图层事件
116 - (void)didTapBackgroundView:(UITapGestureRecognizer *)sender {
117     
118 }
119 
120 #pragma mark - 取消按钮的点击事件
121 - (void)clickLeftBtn {
122     
123 }
124 
125 #pragma mark - 确定按钮的点击事件
126 - (void)clickRightBtn {
127     
128 }
129 
130 @end

  在BaseView.m中主要是对整体框架进行布局,我们的控件的位置都是通过绝对位置进行布局的,所以需要修改的在话可以直接在对应的位置上进行修改,然后在BaseView.h中的注释我们说过了,点击背景遮罩图层和取消、确定按钮的点击事件实现效果在基类中都是空白的,具体效果在子类中进行重写来控制。而对于弹出视图中的标题行(包含取消按钮、确定按钮和标题)、分割线和选择器的具体布局在这里就不进行展开了,很简单的部分,大家自行看一下代码就OK了。

  下面主要提两个问题:一个是整体布局的方法 - (void)initUI 的实现。这里大家主要要注意的添加的层次,谁是谁的子视图,一定要区分清楚。

- (void)initUI {
    self.frame = SCREEN_BOUNDS;
    // 背景遮罩图层
    [self addSubview:self.backgroundView];
    // 弹出视图
    [self addSubview:self.alertView];
    // 设置弹出视图子视图
    // 添加顶部标题栏
    [self.alertView addSubview:self.topView];
    // 添加左边取消按钮
    [self.topView addSubview:self.leftBtn];
    // 添加右边确定按钮
    [self.topView addSubview:self.rightBtn];
    // 添加中间标题按钮
    [self.topView addSubview:self.titleLabel];
    // 添加分割线
    [self.topView addSubview:self.lineView];
}

二是我们的背景蒙层和弹出视图大家可以通过代码看到蒙层遮罩背景的布局是整个屏幕,那么我们为什么不直接在蒙层上添加弹出式图呢?如果直接在蒙层上添加弹出式图作为子视图的话,我们的布局相对会简单很多,这里涉及到一点就是子视图的透明度是和父视图保持一致的,如果直接将弹出视图加载到蒙层遮罩视图上,会导致弹出视图的透明度也为0.3,所以弹出视图不能直接加在蒙层遮罩视图上,而是需要加在当前界面上。

- (UIView *)backgroundView {
    if (!_backgroundView) {
        _backgroundView = [[UIView alloc]initWithFrame:SCREEN_BOUNDS];
        _backgroundView.backgroundColor = [UIColor blackColor] ;
        _backgroundView.alpha = 0.3f ;
        _backgroundView.userInteractionEnabled = YES;
        UITapGestureRecognizer *myTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(didTapBackgroundView:)];
        [_backgroundView addGestureRecognizer:myTap];
    }
    return _backgroundView;
}


    // 背景遮罩图层
    [self addSubview:self.backgroundView];
    // 弹出视图
    [self addSubview:self.alertView];

三 子类选择器实现

  首先是我们的子类向外暴露的方法只有一个类方法,该方法主要是让使用者提供选择器的标题、最小日期、日期选择完成后的操作等基本信息,方便我们对选择器的数据和操作进行设置。对外暴露类方法也是避免使用者在使用时需要创建对象,比较麻烦,也避免一些不必要的问题。

//
//  CJYearMonthSelectedView.h

#import <UIKit/UIKit.h>
#import "BaseView.h"

//日期选择完成之后的操作
typedef void(^BRDateResultBlock)(NSString *selectValue);

@interface CJYearMonthSelectedView : BaseView

//对外开放的类方法
+ (void)showDatePickerWithTitle:(NSString *)title minDateStr:(NSString *)minDateStr resultBlock:(BRDateResultBlock)resultBlock;

@end

  关于具体的子类的实现,还是先把所有代码都贴上来,有点多,所以折叠一下,后面对其中一些要点进行列出说明一下。还有取消、确定按钮的点击事件也都在这里进行控制和实现,我们根据自己的需要进行这是就可以了,一般是在点击确定按钮的时候调用我们的BRDateResultBlock,实现日期选择完成的操作。其中取消按钮就直接没有操作,dismiss当前界面,并注意要进行dealloc,创建的视图要清除,避免内存泄露。蒙层背景点击事件看需求,有的需要和取消一样的效果,有的可能就无效果,自己添加即可。

  1 //  CJYearMonthSelectedView.m
  2 
  3 #import "CJYearMonthSelectedView.h"
  4 
  5 @interface CJYearMonthSelectedView () <UIPickerViewDelegate,UIPickerViewDataSource>
  6 @property (strong, nonatomic) UIPickerView *picker;    //选择器
  7 @property (copy, nonatomic) NSString *title;
  8 @property (copy, nonatomic) NSString *minDateStr;
  9 @property (assign, nonatomic) BRDateResultBlock resultBlock;
 10 @property (copy, nonatomic) NSString *selectValue;   //选择的值 
 11 @property (strong, nonatomic) NSMutableArray<NSString *> *data;
 12 
 13 @end
 14 
 15 @implementation CJYearMonthSelectedView
 16 
 17 + (void)showDatePickerWithTitle:(NSString *)title minDateStr:(NSString *)minDateStr resultBlock:(BRDateResultBlock)resultBlock{
 18     
 19     CJYearMonthSelectedView *datePicker = [[CJYearMonthSelectedView alloc] initWithTitle:title minDateStr:minDateStr resultBlock:resultBlock];
 20     [datePicker showWithAnimation:YES];
 21 }
 22 
 23 //初始化方法
 24 - (instancetype)initWithTitle:(NSString *)title minDateStr:(NSString *)minDateStr resultBlock:(BRDateResultBlock)resultBlock{
 25     if (self = [super init]) {
 26         _title = title;
 27         _minDateStr = minDateStr;
 28         _resultBlock = resultBlock;
 29         
 30         [self initUI];
 31     }
 32     
 33     return self;
 34 }
 35 
 36 //UI布局,主要就是在弹出视图上添加选择器
 37 - (void)initUI{
 38     [super initUI];
 39     self.titleLabel.text = _title;
 40     // 添加时间选择器
 41     [self.alertView addSubview:self.picker];
 42 }
 43 
 44 //选择器的初始化和布局
 45 - (UIPickerView *)picker{
 46     if (!_picker) {
 47         _picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, kTopViewHeight + 0.5, SCREEN_WIDTH, kDatePicHeight)];
 48 //        _picker.backgroundColor = [UIColor whiteColor];
 49         _picker.showsSelectionIndicator = YES;
 50         //设置代理
 51         _picker.delegate =self;
 52         _picker.dataSource =self;
 53     }
 54     return _picker;
 55 }
 56 
 57 //选择器数据的加载,从设定的最小日期到当前月
 58 - (NSMutableArray<NSString *> *)data{
 59     if (!_data) {
 60         _data = [[NSMutableArray alloc] init];
 61         NSDate *currentDate = [NSDate date];
 62         NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
 63         [formatter setDateFormat:@"yyyy-MM"];
 64         NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
 65         NSDateComponents *lastMonthComps = [[NSDateComponents alloc] init];
 66         NSString *dateStr = [formatter stringFromDate:currentDate];
 67         NSInteger lastIndex = 0;
 68         NSDate *newdate;
 69         //循环获取可选月份,从当前月份到最小月份
 70         while (!([dateStr compare:self.minDateStr] == NSOrderedAscending)) {
 71             [_data addObject:dateStr];
 72             lastIndex--;
 73             //获取之前几个月
 74             [lastMonthComps setMonth:lastIndex];
 75             newdate = [calendar dateByAddingComponents:lastMonthComps toDate:currentDate options:0];
 76             dateStr = [formatter stringFromDate:newdate];
 77         }
 78     }
 79     return _data;
 80 }
 81 
 82 #pragma mark - UIPickerView的数据和布局,和tableview类似
 83 //返回多少列
 84 -(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
 85     return 1;
 86 }
 87 
 88 //返回多少行
 89 - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
 90     return self.data.count;
 91 }
 92 
 93 //每一行的数据
 94 -(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
 95     return self.data[row];
 96 }
 97 
 98 //选中时的效果
 99 -(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
100     self.selectValue = self.data[row];
101 }
102 
103 //返回高度
104 -(CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{
105     return 35.0f;
106 }
107 
108 //返回宽度
109 -(CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component{
110     return ZYAppWidth;
111 }
112 
113 #pragma mark - 背景视图的点击事件
114 - (void)didTapBackgroundView:(UITapGestureRecognizer *)sender {
115     //    [self dismissWithAnimation:NO];
116 }
117 
118 #pragma mark - 弹出视图方法
119 - (void)showWithAnimation:(BOOL)animation {
120     //1. 获取当前应用的主窗口
121     UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
122     [keyWindow addSubview:self];
123     if (animation) {
124         // 动画前初始位置
125         CGRect rect = self.alertView.frame;
126         rect.origin.y = SCREEN_HEIGHT;
127         self.alertView.frame = rect;
128         // 浮现动画
129         [UIView animateWithDuration:0.3 animations:^{
130             CGRect rect = self.alertView.frame;
131             rect.origin.y -= kDatePicHeight + kTopViewHeight;
132             self.alertView.frame = rect;
133         }];
134     }
135 }
136 
137 #pragma mark - 关闭视图方法
138 - (void)dismissWithAnimation:(BOOL)animation {
139     // 关闭动画
140     [UIView animateWithDuration:0.2 animations:^{
141         CGRect rect = self.alertView.frame;
142         rect.origin.y += kDatePicHeight + kTopViewHeight;
143         self.alertView.frame = rect;
144         
145         self.backgroundView.alpha = 0;
146     } completion:^(BOOL finished) {
147         [self.leftBtn removeFromSuperview];
148         [self.rightBtn removeFromSuperview];
149         [self.titleLabel removeFromSuperview];
150         [self.lineView removeFromSuperview];
151         [self.topView removeFromSuperview];
152         [self.picker removeFromSuperview];
153         [self.alertView removeFromSuperview];
154         [self.backgroundView removeFromSuperview];
155         [self removeFromSuperview];
156         
157         self.leftBtn = nil;
158         self.rightBtn = nil;
159         self.titleLabel = nil;
160         self.lineView = nil;
161         self.topView = nil;
162         self.picker = nil;
163         self.alertView = nil;
164         self.backgroundView = nil;
165     }];
166 }
167 
168 #pragma mark - 取消按钮的点击事件
169 - (void)clickLeftBtn {
170     [self dismissWithAnimation:YES];
171 }
172 
173 #pragma mark - 确定按钮的点击事件
174 - (void)clickRightBtn {
175     NSLog(@"点击确定按钮后,执行block回调");
176     [self dismissWithAnimation:YES];
177     if (_resultBlock) {
178         _resultBlock(_selectValue);
179     }
180 }
181 
182 @end

  这里面跟着流程看其实很简单哈,主要需要说明的一点就是UIPickerView的用法,UIPickerView其实和UITableView很类似,在初始化的时候需要设置其数据代理和视图代理(UIPickerViewDelegate,UIPickerViewDataSource),然后通过这两个代理进内容、行数、列数的控制。

- (UIPickerView *)picker{
    if (!_picker) {
        _picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, kTopViewHeight + 0.5, SCREEN_WIDTH, kDatePicHeight)];
        _picker.showsSelectionIndicator = YES;
        //设置UIPickerView的代理
        _picker.delegate =self;
        _picker.dataSource =self;
    }
    return _picker;
}
#pragma mark - UIPickerView
//返回多少列
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}

//返回多少行
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return self.data.count;
}

//每一行的数据
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
    return self.data[row];
}

//选中时的效果
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
    self.selectValue = self.data[row];
}

//返回高度
-(CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{
    return 35.0f;
}

//返回宽度
-(CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component{
    return ZYAppWidth;
}

  关于数据的控制可以根据我们的需要进行设定,行数和列数也是根据我们的需求来进行控制。下面主要就是说一下如何获取年月这样的数据,主要是用到了NSDateComponents 的直接获取一个月前的信息,然后通过将NSCalendar将NSDateComponents转化为日期Date,最后将Date转化为我们需要的格式的数据。

//数据获取
- (NSMutableArray<NSString *> *)data{
    if (!_data) {
        _data = [[NSMutableArray alloc] init];
        //当前日期时间
        NSDate *currentDate = [NSDate date];
        //设定数据格式为xxxx-mm
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"yyyy-MM"];
        //通过日历可以直接获取前几个月的日期,所以这里直接用该类的方法进行循环获取数据
        NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        NSDateComponents *lastMonthComps = [[NSDateComponents alloc] init];
        NSString *dateStr = [formatter stringFromDate:currentDate];
        NSInteger lastIndex = 0;
        NSDate *newdate;
        //循环获取可选月份,从当前月份到最小月份,直接用字符串的比较来判断是否大于设定的最小日期
        while (!([dateStr compare:self.minDateStr] == NSOrderedAscending)) {
            [_data addObject:dateStr];
            lastIndex--;
            //获取之前n个月, setMonth的参数为正则向后,为负则表示之前
            [lastMonthComps setMonth:lastIndex];
            newdate = [calendar dateByAddingComponents:lastMonthComps toDate:currentDate options:0];
            dateStr = [formatter stringFromDate:newdate];
        }
    }
    return _data;
}

四 使用方法

关于自己做的这个在使用上就非常简单了,我们的子类向外就暴露了一个类方法,所以我们再需要弹出选择器的地方调用该方法就可以了。

- (void) btnPress:(UIButton *)sender{
    if (sender.tag == 200) { //导出 按钮
        [CJYearMonthSelectedView showDatePickerWithTitle:@"选择月份" minDateStr:@"2017-10" resultBlock:^(NSString *selectValue) {
            //选择完成后的操作
            NSLog(@"selected month is %@", selectValue);
        }];
    } else {
        
    }
}

以上就是使用UIPickerView自定义一个年月的选择器,包括最初的的完整的界面代码和具体的选择器的创建和布局,以及我们的数据处理。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏iOS进阶开发

iOS开发UI篇--iOS动画(Core Animation)总结

IOS 动画主要是指Core Animation框架。官方使用文档地址为:Core Animation Guide。 Core Animation是IOS和OS...

1220
来自专栏陈满iOS

iOS开发·适配iPhone X相关的宏和方法

适配iPhone X和Xcode 9的过程中,除了与导航栏相关的问题,还有一个问题经常出现,就是UITableView相关的问题。下面两个办法可以解决多数错位的...

1464
来自专栏xx_Cc的学习总结专栏

iOS-UIPickerView详解iOS-UIPickerView详解UIPickerView的代理方法

3996
来自专栏iOS开发攻城狮的集散地

自定义UIPageControl、UITextView占位视图

2199
来自专栏Alice

一个layer可以跟着画完的线移动ios程序 好玩啊。

用法:采用的是关键帧实现的。    实验目的:让上层的layer子层能够跟着在另一个子层上花的线进行移动 。即当线画完之后,图形开始移动,并且能够停在最后的那个...

2266
来自专栏一“技”之长

iOS开发CoreAnimation解读之二——对CALayer的分析

        每一个UIView的对象中都有一个layer这样的属性,并且layer会负责view中有关图形绘制的相关操作,例如我们设置view的背景颜色和设...

802
来自专栏Rindew的iOS技术分享

iOS初来乍到,你如何开始第一个封装类?

2344
来自专栏一“技”之长

玩转iOS转场动画 原

    关于动画在iOS开发中的应用,曾经整理过一系列的博客进行总结。包括简单的UIView层的动画,CALayer层的动画,Autolayout自动布局动画以...

5105
来自专栏学海无涯

iOS开发之UIScrollView无限滚动

UIScrollView 的无限滚动主要应用在图片轮播器、欢迎界面等场景。首先需要说明的是,文本所讲的是一种"笨办法",但是好理解且容易实现,在图片不多的时候用...

40010
来自专栏Youngxj

Canvas画板

3764

扫码关注云+社区

领取腾讯云代金券