iOS,自定义控件

1.下拉刷新控件

2.上拉加载更多控件

3.自定义UIAlertView

4.自定义照片选择器(可多选)

下拉刷新控件

 //VRefreshHeadView.h文件

//
//  VRefreshHeadView.h
//  下拉刷新控件
//
//  Created by Vie on 2016/10/10.
//  Copyright © 2016年 Vie. All rights reserved.
//

#import <UIKit/UIKit.h>

typedef NS_ENUM(NSUInteger, VRefreshType) {
    VRefreshTypeDefault = 0,//默认下拉刷新样式
};


@interface VRefreshHeadView : UIView
typedef void (^beginRefreshBlock) (VRefreshHeadView *vrefresh);//刷新事件回调
@property(nonatomic,assign) NSUInteger type;//刷新视图样式

/**
 创建下拉刷新视图

 @param scrollView        滚动视图
 @param beginRefreshBlock 开始刷新回调

 @return 刷新视图对象
 */
-(instancetype)initWithScrollView:(UIScrollView *)scrollView beginRefreshBlock:(beginRefreshBlock)beginRefreshBlock;

//停止刷新
-(void)endRefresh;
@end

 //VRefreshHeadView.m文件

//
//  VRefreshHeadView.m
//  下拉刷新控件
//
//  Created by Vie on 2016/10/10.
//  Copyright © 2016年 Vie. All rights reserved.
//

#import "VRefreshHeadView.h"

#define vRefreshHeadViewHeight 40
#define navBarHeight  64  //竖屏导航栏和控制器高度(竖屏导航栏高度44,竖屏状态栏高度为20、打电话或者某些情况为40)

@interface VRefreshHeadView ()
@property (nonatomic,copy) beginRefreshBlock beginRefreshBlock;
@property(nonatomic,weak) UIScrollView *scrollView;
/**
 *头部提示语
 */
@property(nonatomic,weak) UILabel *headTipLable;
/**
 *  加载提示
 */
@property (nonatomic, weak) UIActivityIndicatorView *indicatorView;
/**
 *是否处于刷新状态
 */
@property(nonatomic,assign) BOOL isRefresh;
@end

@implementation VRefreshHeadView
#pragma mark  - init方法
-(instancetype)initWithScrollView:(UIScrollView *)scrollView beginRefreshBlock:(beginRefreshBlock)beginRefreshBlock{
    self=[super initWithFrame:CGRectMake(0, -vRefreshHeadViewHeight, scrollView.frame.size.width, vRefreshHeadViewHeight)];
    if (self) {
        _scrollView=scrollView;
        _beginRefreshBlock=beginRefreshBlock;
        
        //把当前视图加载到scrollView的UI
        [_scrollView addSubview:self];
        //设置背景颜色
        self.backgroundColor=[UIColor clearColor];
        //默认不在刷新状态
        self.isRefresh=false;
        
        //注册观察者,监听下拉刷新改变
        [_scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
    }
    return self;
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"contentOffset"]) {
        if (_scrollView.contentOffset.y<(-navBarHeight-vRefreshHeadViewHeight)&&!self.isRefresh&&_scrollView.dragging) {
             [self.indicatorView startAnimating];
            self.isRefresh=true;
            self.headTipLable.text=@"加载中..";
            _scrollView.contentInset = UIEdgeInsetsMake(vRefreshHeadViewHeight+navBarHeight, 0, 0, 0);
            if (_beginRefreshBlock) {
                _beginRefreshBlock(self);
            }
        }
    }
}

//停止刷新
-(void)endRefresh{
    [UIView  animateWithDuration:0.3 animations:^{
//        _scrollView.contentInset = UIEdgeInsetsZero;
        _scrollView.contentInset = UIEdgeInsetsMake(navBarHeight, 0, 0, 0);
    } completion:^(BOOL finished) {
        [self.indicatorView stopAnimating];
        self.isRefresh=false;
        _headTipLable.text=@"下拉刷新数据";
    }];
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
 */
- (void)drawRect:(CGRect)rect {
    // Drawing code
    if (_type) {
        
    }else{
        //没有指定type用默认视图
        [self setDefaultView];
    }
}

//默认样式
-(void)setDefaultView{
    [self headTipLable];
}
#pragma mark - 懒加载创建控件

- (UIActivityIndicatorView *)indicatorView
{
    if (!_indicatorView)
    {
        UIActivityIndicatorView *act = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(50, 0, 30, self.frame.size.height)];
        act.color = [UIColor grayColor];
        [self addSubview:act];
        _indicatorView = act;
    }
    
    return _indicatorView;
}
-(UILabel *)headTipLable{
    if (!_headTipLable) {
        UILabel *lb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width,self.frame.size.height)];
        lb.textAlignment = NSTextAlignmentCenter;
        lb.textColor = [UIColor grayColor];
        lb.font = [UIFont systemFontOfSize:13];
        lb.text = @"下拉刷新数据";
        [self addSubview:lb];
        _headTipLable = lb;
    }
    return _headTipLable;
}
-(void)dealloc{
    [_scrollView removeObserver:self forKeyPath:@"contentOffset"];
}

@end

//使用

#import "VRefreshHeadView.h"

@property(nonatomic,strong) VRefreshHeadView *vrefresh;



-(VRefreshHeadView *)vrefresh{
    __weak typeof(self) weakSelf = self;
    if (!_vrefresh) {
        _vrefresh=[[VRefreshHeadView alloc] initWithScrollView:weakSelf.tbView beginRefreshBlock:^(VRefreshHeadView *vrefresh) {
            NSLog(@"进入刷新回调");
            [self performSelector:@selector(endVrefresh) withObject:nil afterDelay:3.0f];

        }];
    }
    
    return _vrefresh;
}

 [self vrefresh];

上拉加载更多控件 

//VLoadMoreFootView.h文件

//
//  VLoadMoreFootView.h
//  上拉加载更多控件
//
//  Created by Vie on 2016/10/17.
//  Copyright © 2016年 Vie. All rights reserved.
//

#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, VLoadMoreType) {
    VLoadMoreTypeDefault = 0,//默认上拉加载样式
};


@interface VLoadMoreFootView : UIView
typedef void (^beginLoadMoreBlock) (VLoadMoreFootView *vrefresh);//加载更多事件回调
@property(nonatomic,assign) NSUInteger type;//加载更多视图样式

/**
 创建上拉加载新视图

 @param scrollView         滚动视图
 @param beginLoadMoreBlock 开始加载更多回调

 @return 加载视图对象
 */
-(instancetype)initWithScrollView:(UIScrollView *)scrollView beginLoadMoreBlock:(beginLoadMoreBlock)beginLoadMoreBlock;


//停止加载
-(void)endLoadMore;

@end

//VLoadMoreFootView.m文件

//
//  VLoadMoreFootView.m
//  上拉加载更多控件
//
//  Created by Vie on 2016/10/17.
//  Copyright © 2016年 Vie. All rights reserved.
//

#import "VLoadMoreFootView.h"

#define  vLoadMoreBottomViewHeight  40
#define navBarHeight  64  //竖屏导航栏和控制器高度(竖屏导航栏高度44,竖屏状态栏高度为20、打电话或者某些情况为40)

@interface VLoadMoreFootView()
@property(nonatomic,copy) beginLoadMoreBlock beginLoadMoreBlock;
@property(nonatomic,weak) UIScrollView *scrollView;
/**
 *底部提示语
 */
@property(nonatomic,weak) UILabel *bottomTipLable;
/**
 *加载提示
 */
@property(nonatomic,weak) UIActivityIndicatorView *indicatorView;
/**
 *是否处于加载状态
 */
@property(nonatomic,assign) BOOL isLoad;
@end


@implementation VLoadMoreFootView
#pragma mark - init方法
-(instancetype)initWithScrollView:(UIScrollView *)scrollView beginLoadMoreBlock:(beginLoadMoreBlock)beginLoadMoreBlock{
    self=[super initWithFrame:CGRectMake(0, 0, scrollView.frame.size.width, vLoadMoreBottomViewHeight)];
    if (self) {
        _scrollView=scrollView;
        _beginLoadMoreBlock=beginLoadMoreBlock;
        
        //把当前视图加载到scrollView的UI
        [_scrollView addSubview:self];
        //设置背景颜色
        self.backgroundColor=[UIColor clearColor];
        //默认不在加载状态
        self.isLoad=false;
        
        //注册观察这,监听上拉加载改变
        [_scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
        
    }
   
    return self;
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"contentOffset"]) {
        if (_scrollView.contentOffset.y>(_scrollView.contentSize.height-_scrollView.frame.size.height)&&!self.isLoad&&_scrollView.dragging) {
            [self.indicatorView startAnimating];
            self.isLoad=true;
            self.bottomTipLable.text=@"加载中..";
            _scrollView.contentInset = UIEdgeInsetsMake(0, 0, vLoadMoreBottomViewHeight, 0);
            if (_beginLoadMoreBlock) {
                _beginLoadMoreBlock(self);
            }
        }
    }
}

//停止加载
-(void)endLoadMore{
    [UIView animateWithDuration:0.3 animations:^{
//        _scrollView.contentInset = UIEdgeInsetsZero;
        _scrollView.contentInset = UIEdgeInsetsMake(navBarHeight, 0, 0, 0);
    } completion:^(BOOL finished) {
        [self.indicatorView stopAnimating];
        self.isLoad=false;
         _bottomTipLable.text = @"上拉加载更多数据";
    }];
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
*/

- (void)drawRect:(CGRect)rect {
    // Drawing code
    if (_type) {
        
    }else{
       //没有指定type用默认视图
        [self setDefaultView];
    }
}

//默认样式
-(void)setDefaultView{
    [self bottomTipLable];
}

-(UILabel *)bottomTipLable{
    if (!_bottomTipLable) {
        UILabel *lb = [[UILabel alloc] initWithFrame:CGRectMake(0, _scrollView.contentSize.height, self.frame.size.width,self.frame.size.height)];
        lb.textAlignment = NSTextAlignmentCenter;
        lb.textColor = [UIColor grayColor];
        lb.font = [UIFont systemFontOfSize:13];
        lb.text = @"上拉加载更多数据";
        [self addSubview:lb];
        _bottomTipLable = lb;
    }
    return _bottomTipLable;
}

#pragma mark - 懒加载创建控件

- (UIActivityIndicatorView *)indicatorView
{
    if (!_indicatorView)
    {
        UIActivityIndicatorView *act = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(50, _scrollView.contentSize.height, 30, self.frame.size.height)];
        act.color = [UIColor grayColor];
        [self addSubview:act];
        _indicatorView = act;
    }
    
    return _indicatorView;
}
-(void)dealloc{
    [_scrollView removeObserver:self forKeyPath:@"contentOffset"];
}
@end

//使用

#import "VLoadMoreFootView.h"


@property(nonatomic,strong) VLoadMoreFootView *vload;

-(VLoadMoreFootView *)vload{
    __weak typeof(self) weakSelf=self;
    if (!_vload) {
        _vload=[[VLoadMoreFootView alloc] initWithScrollView:weakSelf.tbView beginLoadMoreBlock:^(VLoadMoreFootView *vrefresh) {
            NSLog(@"进入上拉加载");
            [self performSelector:@selector(endVload) withObject:nil afterDelay:3.0f];

        }];
    }
    return _vload;
}

-(void)endVload{
    [_vload endLoadMore];
}

 [self vload];

 自定义UIAlertView

//TKIMAlertView.h文件

//
//  TKIMAlertView.h
//  泰隆微信
//  自定义alertview
//  Created by Vie on 2017/1/18.
//  Copyright © 2017年 liupm. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface TKIMAlertView : UIAlertView
@property(nonatomic, assign) NSTextAlignment textAlignment;//文本对齐方式
@end

//TKIMAlertView.m文件

//
//  TKIMAlertView.m
//  泰隆微信
//  自定义alertview
//  Created by Vie on 2017/1/18.
//  Copyright © 2017年 liupm. All rights reserved.
//

#import "TKIMAlertView.h"

@implementation TKIMAlertView

//iOS7以后获取alertview的subviews为空
-(void)show{
    [super show];

   
    
    float alertWith=268;
    float textLabelX=20;
    //文本占用空间
     CGSize messageSize=[self.message boundingRectWithSize:CGSizeMake(alertWith-textLabelX-5, 500) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15]} context:nil].size;
    messageSize.height+=15;
    //文本
    UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(textLabelX, 0, alertWith-textLabelX, messageSize.height)];
    textLabel.font = [UIFont systemFontOfSize:15];
    textLabel.textColor = [UIColor blackColor];
    textLabel.backgroundColor = [UIColor clearColor];
    textLabel.lineBreakMode =NSLineBreakByWordWrapping;
    textLabel.numberOfLines =0;
    textLabel.textAlignment =_textAlignment;
    textLabel.text =self.message;
    
    //文本背景视图
    UIScrollView *view=[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, alertWith, messageSize.height)];
    view.contentSize=CGSizeMake(alertWith, messageSize.height);
    //指定控件是否只能在一个方向上滚动(默认为NO)
    view.directionalLockEnabled=YES;
    [view addSubview:textLabel];
    
    [self setValue:view forKey:@"accessoryView"];
    
    self.message=@"";

}
@end

//使用

 TKIMAlertView *alert= [[TKIMAlertView alloc] initWithTitle:[NSString stringWithFormat:@"新版本升级 %@",self.versionModel.version] message:@"1.新版
2.修复Bug
" delegate:self cancelButtonTitle:nil otherButtonTitles:@"升级", nil];
 alert.textAlignment=NSTextAlignmentLeft;
 [alert show];

自定义照片选择器(可多选)

//ChoosePhotosCollectionViewCell.h文件

//
//  ChoosePhotosCollectionViewCell.h
//  CustomChoosePhotoDemo
//  照片展示器单元格
//  Created by Vie on 2017/2/15.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "ChoosePhotosBlock.h"
@interface ChoosePhotosCollectionViewCell : UICollectionViewCell

@property (strong, nonatomic)UIImageView * photoImageView;//展示缩略图
@property (nonatomic, assign) BOOL chooseFlag;//是否被选中,初始化为否
@property (nonatomic, copy) ChoosePhotosFlag chooseBlock;
//未被选中
-(void)didUncheck;
//被选中
-(void)didSelected;
@end

//ChoosePhotosCollectionViewCell.m文件

//
//  ChoosePhotosCollectionViewCell.m
//  CustomChoosePhotoDemo
//  照片展示器单元格
//  Created by Vie on 2017/2/15.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import "ChoosePhotosCollectionViewCell.h"

@interface ChoosePhotosCollectionViewCell ()
@property (strong, nonatomic) UIButton *chooseBtn;//选中按钮
@end

@implementation ChoosePhotosCollectionViewCell
#pragma mark 懒加载
-(UIImageView *)photoImageView{
    if (!_photoImageView) {
        _photoImageView = [[UIImageView alloc]init];
        _photoImageView.translatesAutoresizingMaskIntoConstraints = false;
        _photoImageView.contentMode = UIViewContentModeScaleAspectFill;
        _photoImageView.layer.masksToBounds = true;
    }
    return _photoImageView;
}
//显示选择按钮的视图
- (UIButton *)chooseBtn
{
    if (_chooseBtn == nil)
    {
        _chooseBtn= [UIButton buttonWithType:UIButtonTypeCustom];
        _chooseBtn.translatesAutoresizingMaskIntoConstraints = false;
        [_chooseBtn addTarget:self action:@selector(chooseAction:) forControlEvents:UIControlEventTouchUpInside];
        _chooseBtn.translatesAutoresizingMaskIntoConstraints = false;
        [_chooseBtn setImage:[UIImage imageNamed:@"uncheck.png"] forState:UIControlStateNormal];
    }
    return _chooseBtn;
}
#pragma mark 处理界面
-(instancetype)initWithFrame:(CGRect)frame{
    self=[super initWithFrame:frame];
    if (self) {
        [self loadView];
        [self layoutVFL];
    }
    return self;
}
/*加载视图*/
-(void)loadView{
    [self addSubview:self.photoImageView];
    [self addSubview:self.chooseBtn];
}

/*VFL约束*/
-(void)layoutVFL{
    /*photoImageView的约束*/
    //水平约束
    NSArray * photoImageViewHorizotal = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[_photoImageView]-0-|" options:0 metrics:nil views:@{@"_photoImageView":_photoImageView}];
    [self addConstraints:photoImageViewHorizotal];
    //垂直约束
    NSArray * photoImageViewVertical = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[_photoImageView]-0-|" options:0 metrics:nil views:@{@"_photoImageView":_photoImageView}];
    [self addConstraints:photoImageViewVertical];
    
    NSArray * chooseBtnViewHorizotal = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[_chooseBtn(22)]-5-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_chooseBtn)];
    [self addConstraints:chooseBtnViewHorizotal];
    NSArray * chooseBtnViewVertical = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-5-[_chooseBtn(22)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_chooseBtn)];
    [self addConstraints:chooseBtnViewVertical];
}
#pragma mark 事件处理
-(void)chooseAction:(UIButton *)sender{
    if (self.chooseFlag) {
        [_chooseBtn setImage:[UIImage imageNamed:@"uncheck.png"] forState:UIControlStateNormal];
        self.chooseFlag=false;
    }else{
        [_chooseBtn setImage:[UIImage imageNamed:@"selected.png"] forState:UIControlStateNormal];
        self.chooseFlag=true;
    }
    _chooseBlock(self.chooseFlag);
}

-(void)didUncheck{
    [_chooseBtn setImage:[UIImage imageNamed:@"uncheck.png"] forState:UIControlStateNormal];
    self.chooseFlag=false;
}
-(void)didSelected{
    [_chooseBtn setImage:[UIImage imageNamed:@"selected.png"] forState:UIControlStateNormal];
    self.chooseFlag=true;
}
@end

//ChoosePhotoGroupTableViewCell.h文件

//
//  ChoosePhotoGroupTableViewCell.h
//  CustomChoosePhotoDemo
//  相册簿组群单元格
//  Created by Vie on 2017/2/15.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <AssetsLibrary/AssetsLibrary.h>
@interface ChoosePhotoGroupTableViewCell : UITableViewCell
@property (nonatomic ,strong) ALAssetsGroup * group;//相册簿组群
@end

//ChoosePhotoGroupTableViewCell.m文件

//
//  ChoosePhotoGroupTableViewCell.m
//  CustomChoosePhotoDemo
//  相册簿组群单元格
//  Created by Vie on 2017/2/15.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import "ChoosePhotoGroupTableViewCell.h"
@interface ChoosePhotoGroupTableViewCell()
@property (nonatomic, strong) UIImageView *headImgView;//相册簿群组缩略图
@property (nonatomic, strong) UILabel *groupNameLable;//相册簿群组名称
@property (nonatomic, strong) UILabel *numberLable;//相册簿群组照片数量
@end

@implementation ChoosePhotoGroupTableViewCell
-(void)setGroup:(ALAssetsGroup *)group{
    if (group) {
        //获得当前的预览图
        CGImageRef imageRef = [group posterImage];
        UIImage * image = [UIImage imageWithCGImage:imageRef];
        self.headImgView.image=image;
        
        self.groupNameLable.text=NSLocalizedString([group valueForProperty:ALAssetsGroupPropertyName],@"");
        self.numberLable.text=[NSString stringWithFormat:@"%@",@([group numberOfAssets])];
    }
    
}
#pragma mark   懒加载
-(UIImageView *)headImgView{
    if (!_headImgView) {
        _headImgView=[[UIImageView alloc] init];
        _headImgView.translatesAutoresizingMaskIntoConstraints=NO;
    }
    return _headImgView;
}
-(UILabel *)groupNameLable{
    if (!_groupNameLable) {
        _groupNameLable=[[UILabel alloc] init];
        _groupNameLable.translatesAutoresizingMaskIntoConstraints=NO;
        _groupNameLable.font=[UIFont systemFontOfSize:14];
    }
    return _groupNameLable;
}
-(UILabel *)numberLable{
    if (!_numberLable) {
        _numberLable=[[UILabel alloc] init];
        _numberLable.translatesAutoresizingMaskIntoConstraints=NO;
        _numberLable.font=[UIFont systemFontOfSize:12];
    }
    return _numberLable;
}
#pragma mark   视图处理
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
    self=[super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self loadView];
        [self layoutVFL];
    }
    return self;
}
/*加载视图*/
-(void)loadView{
    [self addSubview:self.headImgView];
    [self addSubview:self.groupNameLable];
    [self addSubview:self.numberLable];
}
/*VFL约束*/
-(void)layoutVFL{
    //相册簿群组缩略图约束
    /*Horizontal距离左边20*/
    NSArray *headImgViewHorizotal=[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[_headImgView]" options:0 metrics:nil views:@{@"_headImgView":_headImgView}];
    [self addConstraints:headImgViewHorizotal];
    /*Vertical距离顶部和底部各10个距离等于设置了高度*/
    NSArray *headImgViewVertical=[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_headImgView]-10-|" options:0 metrics:nil views:@{@"_headImgView":_headImgView}];
    [self addConstraints:headImgViewVertical];
    /*设置headImgView宽等于高度*/
    NSLayoutConstraint *headImgViewConstraint=[NSLayoutConstraint constraintWithItem:_headImgView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_headImgView attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
    [self addConstraint:headImgViewConstraint];
    
    
    //相册簿群组名称约束groupNameLable
    /*_groupNameLable距离左边_headImgView20个单位,距离右边20个距离,等于设置了宽度*/
    NSArray *groupNameLableHorizontal=[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_headImgView]-20-[_groupNameLable]-20-|" options:0 metrics:nil views:@{@"_headImgView":_headImgView,@"_groupNameLable":_groupNameLable}];
    [self addConstraints:groupNameLableHorizontal];
    /*_groupNameLable距离顶部10个单位,高度为21*/
    NSArray *groupNameLableVertical=[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_groupNameLable(21)]" options:0 metrics:nil views:@{@"_groupNameLable":_groupNameLable}];
    [self addConstraints:groupNameLableVertical];
    
    //相册簿群组照片数量约束
    /*_numberLable左边距离_headImgView20个距离,距离右边20个距离,等于设置了宽度*/
    NSArray *numberLableHorizontal=[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_headImgView]-20-[_numberLable]-20-|" options:0 metrics:nil views:@{@"_headImgView":_headImgView,@"_numberLable":_numberLable}];
    [self addConstraints:numberLableHorizontal];
    /*_numberLable顶部距离_groupNameLable有0个距离,高度15*/
    NSArray *numberLableVertical=[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_groupNameLable]-0-[_numberLable(15)]" options:0 metrics:nil views:@{@"_groupNameLable":_groupNameLable,@"_numberLable":_numberLable}];
    [self addConstraints:numberLableVertical];
}

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

@end

//ChoosePhotosBlock.h文件

//
//  ChoosePhotosBlock.h
//  CustomChoosePhotoDemo
//  block定义
//  Created by Vie on 2017/2/15.
//  Copyright © 2017年 Vie. All rights reserved.
//

#ifndef ChoosePhotosBlock_h
#define ChoosePhotosBlock_h

typedef void(^ChoosePhotosFlag)(BOOL flag) ;//是否选中状态回调
typedef void(^ReturnImagesArray)(NSArray<UIImage *>* choosedImages);//选中照片返回
#endif /* ChoosePhotosBlock_h */

//BrowseChooseImgViewController.h文件

//
//  BrowseChooseImgViewController.h
//  CustomChoosePhotoDemo
//  浏览点击的图片
//  Created by Vie on 2017/2/16.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface BrowseChooseImgViewController : UIViewController

@property (nonatomic, strong) UIImageView * tempImgView;//显示一个当前点击的图
@end

//BrowseChooseImgViewController.m文件

//
//  BrowseChooseImgViewController.m
//  CustomChoosePhotoDemo
//  浏览点击的图片
//  Created by Vie on 2017/2/16.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import "BrowseChooseImgViewController.h"

@interface BrowseChooseImgViewController ()

@end

@implementation BrowseChooseImgViewController
-(instancetype)init{
    self=[super init];
    if (self) {
        [self.view addSubview:self.tempImgView];
        [self layoutVFL];
    }
    return self;
}
#pragma mark 懒加载
-(UIImageView *)tempImgView{
    if (!_tempImgView) {
        _tempImgView = [[UIImageView alloc] init];
        _tempImgView.contentMode = UIViewContentModeScaleAspectFit;
        _tempImgView.backgroundColor = [UIColor blackColor];
        _tempImgView.translatesAutoresizingMaskIntoConstraints=NO;
    }
    return _tempImgView;
}
#pragma mark 界面处理
- (void)viewDidLoad {
    [super viewDidLoad];
    

}

-(void)layoutVFL{
    /*tableview水平方法距离两边各0距离等于设了x值和宽度*/
    NSArray *tableViewHorizontal=[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[_tempImgView]-0-|" options:0 metrics:nil views:@{@"_tempImgView":_tempImgView}];
    [self.view addConstraints:tableViewHorizontal];
    /*tableview垂直方向距离两边各0距离等于设置了y值和高度*/
    NSArray *tableViewVertical=[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[_tempImgView]-0-|" options:0 metrics:nil views:@{@"_tempImgView":_tempImgView}];
    [self.view addConstraints:tableViewVertical];
}

@end

//ChoosePhotosCollectionViewController.h文件

//
//  ChoosePhotosCollectionViewController.h
//  CustomChoosePhotoDemo
//  照片展示器
//  Created by Vie on 2017/2/15.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import "ChoosePhotosBlock.h"

@interface ChoosePhotosCollectionViewController : UIViewController
@property (nonatomic, strong) NSMutableArray <ALAsset *> * photos;//存储ALAsset对象的数组
@property (nonatomic, copy) ReturnImagesArray returnImagesArray;//确认选中照片回调
@end

//ChoosePhotosCollectionViewController.m文件

//
//  ChoosePhotosCollectionViewController.m
//  CustomChoosePhotoDemo
//  照片展示器
//  Created by Vie on 2017/2/15.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import "ChoosePhotosCollectionViewController.h"
#import "ChoosePhotosCollectionViewCell.h"
#import "BrowseChooseImgViewController.h"

@interface ChoosePhotosCollectionViewController ()<UICollectionViewDelegate,UICollectionViewDataSource>
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, assign) CGFloat cellLength;  //标准大小
@property (nonatomic, strong) NSMutableArray<UIImage *> *choosedImages;//存放选中的图片对象
@property (nonatomic, strong) NSMutableArray<NSNumber *> *imagesFlag;//所以图片的标志位,记录是否被选中,避免在cell记录导致重用错乱问题
@property (nonatomic, strong) NSMutableArray<UIImage *> *allImages;//全部图片
@end

@implementation ChoosePhotosCollectionViewController
-(void)setPhotos:(NSMutableArray<ALAsset *> *)photos{
    if (photos) {
        _photos=photos;
        [self loadImagesFlag:_photos.count];
        [self loadAllImages];
        [self.collectionView reloadData];
    }
}
/*初始化标识为计数数组*/
-(void)loadImagesFlag:(NSInteger)count{
    self.imagesFlag=[[NSMutableArray alloc] init];
    for (int i=0; i<count; i++) {
        [self.imagesFlag addObject:[NSNumber numberWithBool:false]];
    }
}
/*加载所有的图片对象*/
- (void)loadAllImages
{
    self.allImages=[[NSMutableArray alloc] init];
    //进行资源处理
    for (ALAsset * asset in self.photos)
    {
        //这里就不要用thumbnail属性了,太模糊
        [self.allImages addObject:[UIImage imageWithCGImage:asset.aspectRatioThumbnail]];
        
    }
}
#pragma mark 懒加载
-(UICollectionView *)collectionView{
    if (!_collectionView) {
        UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
        _collectionView= [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
        _collectionView.translatesAutoresizingMaskIntoConstraints=NO;
        _collectionView.dataSource=self;
        _collectionView.delegate=self;
        //注册单元格使用的类型,必须先注册,否则会报异常
        [_collectionView registerClass:[ChoosePhotosCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([ChoosePhotosCollectionViewCell class])];
        //注册头
        [_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header"];
        //注册脚
        [_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"footer"];
    }
    return _collectionView;
}

-(NSMutableArray<UIImage *> *)choosedImages{
    if (!_choosedImages) {
        _choosedImages=[[NSMutableArray alloc] init];
    }
    return _choosedImages;
}

#pragma mark 视图处理
- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.rightBarButtonItem=[[UIBarButtonItem alloc] initWithTitle:@"选择" style:UIBarButtonItemStylePlain target:self action:@selector(endChooseReturnArray)];
    //初始化数据,适配横屏
    CGFloat minLength = MIN(self.view.bounds.size.width, self.view.bounds.size.height);
    self.cellLength = (minLength - 3) / 4.0f;
    
    [self.view addSubview:self.collectionView];
    [self layoutVFL];
}
/*使用VIF设置界面自动布局*/
-(void)layoutVFL{
    
    
    /*tableview水平方法距离两边各0距离等于设了x值和宽度*/
    NSArray *tableViewHorizontal=[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[_collectionView]-0-|" options:0 metrics:nil views:@{@"_collectionView":_collectionView}];
    [self.view addConstraints:tableViewHorizontal];
    /*tableview垂直方向距离两边各0距离等于设置了y值和高度*/
    NSArray *tableViewVertical=[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[_collectionView]-0-|" options:0 metrics:nil views:@{@"_collectionView":_collectionView}];
    [self.view addConstraints:tableViewVertical];
}
#pragma mark UICollectionViewDelegate
/*设置分区*/
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}
/*设置分区内单元格数*/
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return self.photos.count;
}
/*设置每个分区的边距*/
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(nonnull UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    return UIEdgeInsetsMake(0, 0, 0, 0);
}
/*item垂直间距*/
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
    return 1;
}
/*item水平间距*/
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
    return 1;
}
/*设置每个item大小*/
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(nonnull UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(nonnull NSIndexPath *)indexPath{
    return CGSizeMake(self.cellLength, self.cellLength);
}
/*设置item*/
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath{
    //设置的单元格要与Identifier注册的一致
    ChoosePhotosCollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([ChoosePhotosCollectionViewCell class]) forIndexPath:indexPath];
    //检查cell是否被选中
    [self cellIsSelect:cell flag:self.imagesFlag[indexPath.row].boolValue];
    
    cell.chooseBlock=^(BOOL flag){
        //记录选中图片状态
        [self.imagesFlag replaceObjectAtIndex:indexPath.row withObject:[NSNumber numberWithBool:flag]];
        if (flag) {
            NSLog(@"***********选中第%ld张图片",indexPath.row+1);
            //添加选中图片
            [self.choosedImages addObject:self.allImages[indexPath.item]];
        }else{
            NSLog(@"***********取消第%ld张图片",indexPath.row+1);
            //移除选中图片
            [self.choosedImages removeObject:self.allImages[indexPath.item]];
        }
    };
    
    //这里尽量不用thumbnail属性,因为太模糊了,可以用aspectRatioThumbnail比例缩略图代替
    cell.photoImageView.image = [UIImage imageWithCGImage:self.photos[indexPath.row].aspectRatioThumbnail];
    return cell;
}
/*点击item*/
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    //获取当前的资源对象
    ALAsset * asset = self.photos[indexPath.item];
    
    //创建浏览控制器
    BrowseChooseImgViewController * ctr = [[BrowseChooseImgViewController alloc] init];
    
    UIImage *image=[UIImage imageWithCGImage:asset.defaultRepresentation.fullScreenImage];
    //设置大图
    ctr.tempImgView.image = [UIImage imageWithCGImage:asset.defaultRepresentation.fullScreenImage];
    
    //弹出
    [self.navigationController pushViewController:ctr animated:true];

}

#pragma mark 事件处理
/*检查item是否被点击过*/
-(void)cellIsSelect:(ChoosePhotosCollectionViewCell *)cell flag:(BOOL)flag{
    if (flag) {
        [cell didSelected];
    }else{
        [cell didUncheck];
    }
}

/*选取照片完成*/
-(void)endChooseReturnArray{
    _returnImagesArray(self.choosedImages);
    //向前返回2个页面,返回到直接选择照片的页面
     [self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:([self.navigationController.viewControllers count]-3)] animated:YES];
}

@end

//ChoosePhotoGroupViewController.h文件

//
//  ChoosePhotoGroupViewController.h
//  CustomChoosePhotoDemo
//  相册薄组群展示器
//  Created by Vie on 2017/2/15.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "ChoosePhotosBlock.h"
@interface ChoosePhotoGroupViewController : UIViewController
@property (nonatomic, copy) ReturnImagesArray returnImagesArray;//确认选中照片回调
@end

//ChoosePhotoGroupViewController.m文件

//
//  ChoosePhotoGroupViewController.m
//  CustomChoosePhotoDemo
//  相册薄组群展示器
//  Created by Vie on 2017/2/15.
//  Copyright © 2017年 Vie. All rights reserved.
//

#import "ChoosePhotoGroupViewController.h"
#import <AssetsLibrary/AssetsLibrary.h>
#import "ChoosePhotoGroupTableViewCell.h"
#import "ChoosePhotosCollectionViewController.h"

@interface ChoosePhotoGroupViewController ()<UITableViewDelegate,UITableViewDataSource>
@property (nonatomic, strong) ALAssetsLibrary *assetsLibrary;//访问控制的视频和照片的照片实例
@property (nonatomic, strong) NSMutableArray <ALAssetsGroup *> *photoAlbumGroups;//存放相册簿组群的数组对象
@property (nonatomic, strong) UITableView *tableView;//表格视图
@end

@implementation ChoosePhotoGroupViewController
#pragma mark  懒加载
-(ALAssetsLibrary *)assetsLibrary{
    if (!_assetsLibrary) {
        _assetsLibrary=[[ALAssetsLibrary alloc] init];
    }
    return _assetsLibrary;
}
-(NSMutableArray<ALAssetsGroup *>*)photoAlbumGroups{
    if (!_photoAlbumGroups) {
        _photoAlbumGroups=[[NSMutableArray alloc] init];
        [self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
            //如果返回的组群存在,分次返回,所以在这里收到数据时候重载画面
            if (group) {
                //对group进行过滤只要照片
                [group setAssetsFilter:[ALAssetsFilter allPhotos]];
                [_photoAlbumGroups addObject:group];
            }
            /*获得相册簿群组后重载数据*/
            [self.tableView reloadData];
        } failureBlock:^(NSError *error) {
            NSLog(@"获得相册簿组群失败");
        }];
    }
    return _photoAlbumGroups;
}
-(UITableView *)tableView{
    if (!_tableView) {
        _tableView=[[UITableView alloc] init];
        _tableView.delegate=self;
        _tableView.dataSource=self;
        /*禁用autoresizing*/
        _tableView.translatesAutoresizingMaskIntoConstraints=NO;
    }
    return _tableView;
}
#pragma mark 界面处理
- (void)viewDidLoad {
    [super viewDidLoad];
    
    /*要在加载到父视图上后设置VFL才有参照系*/
    [self.view addSubview:self.tableView];
    [self layoutVFL];
    
    
}
/*使用VIF设置界面自动布局*/
-(void)layoutVFL{
    
    
    /*tableview水平方法距离两边各0距离等于设了x值和宽度*/
    NSArray *tableViewHorizontal=[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[_tableView]-0-|" options:0 metrics:nil views:@{@"_tableView":_tableView}];
    [self.view addConstraints:tableViewHorizontal];
    /*tableview垂直方向距离两边各0距离等于设置了y值和高度*/
    NSArray *tableViewVertical=[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[_tableView]-0-|" options:0 metrics:nil views:@{@"_tableView":_tableView}];
    [self.view addConstraints:tableViewVertical];
}
#pragma mark UITableViewDelegate
/*设置每个分区多少行*/
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.photoAlbumGroups.count;
}
/*设置分区*/
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}
/*单元格选中事件*/
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
   

    ChoosePhotosCollectionViewController *collectionCtr=[[ChoosePhotosCollectionViewController alloc] init];
    collectionCtr.returnImagesArray=^(NSArray<UIImage *>* choosedImages){
        NSLog(@"*******共选中%ld张图片",choosedImages.count);
        _returnImagesArray(choosedImages);
    };
     [self.navigationController pushViewController:collectionCtr animated:YES];
    
    NSMutableArray *photos=[[NSMutableArray alloc] init];
    //获取group模型
    ALAssetsGroup *group = self.photoAlbumGroups[indexPath.row];
    dispatch_async(dispatch_get_main_queue(), ^{
        //开始读取
        [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
            
            //如果不为空或者媒体为图片
            if (result != nil && [[result valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto])
            {
                //添加数据
                [photos addObject:result];
                
                //数目达标后统一进行赋值
                if (index == group.numberOfAssets - 1)
                {
                    collectionCtr.photos=photos;
                    
                }
                
            }
        }];

    });
    
}
/*设置单元格*/
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    ChoosePhotoGroupTableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:[NSString stringWithFormat:@"%ld",indexPath.row]];
    if (!cell) {
        cell=[[ChoosePhotoGroupTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[NSString stringWithFormat:@"%ld",indexPath.row]];
    }
    //获得当前的数据
    ALAssetsGroup * group = self.photoAlbumGroups[indexPath.row];
    cell.group=group;
    return cell;
}
/*设置单元格高度*/
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 80;
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

//图片资源

//使用

#import "ChoosePhotoGroupViewController.h"
#import "ChoosePhotosBlock.h"




 /*加载照片群组控制器*/
    ChoosePhotoGroupViewController *photoCtr=[[ChoosePhotoGroupViewController alloc] init];
    photoCtr.returnImagesArray=^(NSArray<UIImage *>* choosedImages){
        NSLog(@"***********一共选中%ld张图片",choosedImages.count);
    };
    [self.navigationController pushViewController:photoCtr animated:YES];

//效果展示

原文地址:https://www.cnblogs.com/douniwanxia/p/5969808.html