jsdw_ios/Pods/BRPickerView/BRPickerView/TextPicker/BRTextPickerView.m

624 lines
24 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// BRTextPickerView.m
// BRPickerViewDemo
//
// Created by renbo on 2017/8/11.
// Copyright © 2017 irenb. All rights reserved.
//
// 最新代码下载地址https://github.com/agiapp/BRPickerView
#import "BRTextPickerView.h"
@interface BRTextPickerView ()<UIPickerViewDelegate, UIPickerViewDataSource>
{
BOOL _isSyncingRollingSelection; // 滚动未结束时批量同步,抑制 change 回调
}
/** 选择器 */
@property (nonatomic, strong) UIPickerView *pickerView;
/** 当前显示的数据源 */
@property (nonatomic, copy) NSArray *dataList;
@end
@implementation BRTextPickerView
#pragma mark - 1.显示【单列】选择器
+ (void)showPickerWithTitle:(NSString *)title
dataSourceArr:(NSArray *)dataSourceArr
selectIndex:(NSInteger)selectIndex
resultBlock:(BRSingleResultBlock)resultBlock {
// 创建选择器
BRTextPickerView *strPickerView = [[BRTextPickerView alloc]init];
strPickerView.pickerMode = BRTextPickerComponentSingle;
strPickerView.title = title;
strPickerView.dataSourceArr = dataSourceArr;
strPickerView.selectIndex = selectIndex;
strPickerView.singleResultBlock = resultBlock;
// 显示
[strPickerView show];
}
#pragma mark - 2.显示【多列】选择器
+ (void)showMultiPickerWithTitle:(NSString *)title
dataSourceArr:(NSArray *)dataSourceArr
selectIndexs:(NSArray <NSNumber *>*)selectIndexs
resultBlock:(BRMultiResultBlock)resultBlock {
// 创建选择器
BRTextPickerView *strPickerView = [[BRTextPickerView alloc]init];
strPickerView.pickerMode = BRTextPickerComponentMulti;
strPickerView.title = title;
strPickerView.dataSourceArr = dataSourceArr;
strPickerView.selectIndexs = selectIndexs;
strPickerView.multiResultBlock = resultBlock;
// 显示
[strPickerView show];
}
#pragma mark - 3.显示【联动】选择器
+ (void)showCascadePickerWithTitle:(nullable NSString *)title
dataSourceArr:(nullable NSArray *)dataSourceArr
selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
resultBlock:(nullable BRMultiResultBlock)resultBlock {
// 创建选择器
BRTextPickerView *strPickerView = [[BRTextPickerView alloc]init];
strPickerView.pickerMode = BRTextPickerComponentCascade;
strPickerView.title = title;
strPickerView.dataSourceArr = dataSourceArr;
strPickerView.selectIndexs = selectIndexs;
strPickerView.multiResultBlock = resultBlock;
// 显示
[strPickerView show];
}
#pragma mark - 初始化自定义选择器
- (instancetype)initWithPickerMode:(BRTextPickerMode)pickerMode {
if (self = [super init]) {
self.pickerMode = pickerMode;
}
return self;
}
#pragma mark - 处理选择器数据 和 默认选择状态
- (void)handlerPickerData {
// 1.检查数据源数据格式是否有误
BOOL dataSourceError = NO;
if (self.dataSourceArr.count == 0) {
dataSourceError = YES;
}
id item = [self.dataSourceArr firstObject];
if (self.pickerMode == BRTextPickerComponentSingle) {
dataSourceError = !([item isKindOfClass:[NSString class]] || [item isKindOfClass:[BRTextModel class]]);
} else if (self.pickerMode == BRTextPickerComponentMulti) {
dataSourceError = ![item isKindOfClass:[NSArray class]];
} else if (self.pickerMode == BRTextPickerComponentCascade) {
dataSourceError = ![item isKindOfClass:[BRTextModel class]];
}
if (dataSourceError) {
BRErrorLog(@"数据源异常!请检查选择器数据源的格式");
return;
}
// 2.处理默认选择状态
if (self.pickerMode == BRTextPickerComponentSingle) {
self.dataList = self.dataSourceArr;
if (self.selectIndex < 0 || self.selectIndex >= self.dataList.count) {
self.selectIndex = 0;
}
} else if (self.pickerMode == BRTextPickerComponentMulti) {
self.dataList = self.dataSourceArr;
NSMutableArray *selectIndexs = [[NSMutableArray alloc]init];
for (NSInteger component = 0; component < self.dataList.count; component++) {
NSArray *itemArr = self.dataList[component];
NSInteger row = 0;
if (self.selectIndexs.count > 0 && component < self.selectIndexs.count) {
NSInteger index = [self.selectIndexs[component] integerValue];
row = (index >= 0 && index < itemArr.count) ? index : 0;
}
[selectIndexs addObject:@(row)];
}
self.selectIndexs = [selectIndexs copy];
} else if (self.pickerMode == BRTextPickerComponentCascade) {
NSMutableArray *dataList = [[NSMutableArray alloc]init];
[dataList addObject:self.dataSourceArr];
NSMutableArray *selectIndexs = [[NSMutableArray alloc]init];
BOOL hasNext = self.dataSourceArr.count > 0;
NSInteger i = 0;
NSInteger selectIndex = self.selectIndexs.count > 0 && i < self.selectIndexs.count ? [self.selectIndexs[i] integerValue] : 0;
[selectIndexs addObject:@(selectIndex)];
if (selectIndex >= 0 && selectIndex < self.dataSourceArr.count) {
BRTextModel *selectModel = self.dataSourceArr[selectIndex];
while (hasNext) {
NSArray *nextArr = selectModel.children;
if (!nextArr || nextArr.count == 0) {
hasNext = NO;
break;
}
[dataList addObject:nextArr];
i++;
selectIndex = self.selectIndexs.count > 0 && i < self.selectIndexs.count ? [self.selectIndexs[i] integerValue] : 0;
[selectIndexs addObject:@(selectIndex)];
if (selectIndex < nextArr.count) {
selectModel = nextArr[selectIndex];
}
}
}
// 控制选择器固定显示的列数
if (self.showColumnNum > 0) {
NSInteger dataListCount = dataList.count;
if (self.showColumnNum < dataListCount) {
// 显示子集数据
dataList = [[dataList subarrayWithRange:NSMakeRange(0, self.showColumnNum)] mutableCopy];
selectIndexs = [[selectIndexs subarrayWithRange:NSMakeRange(0, self.showColumnNum)] mutableCopy];
} else {
// 补全占位数据
for (NSInteger i = 0; i < self.showColumnNum - dataListCount; i++) {
// 添加空白占位数据
BRTextModel *placeholderModel = [[BRTextModel alloc]init];
NSArray *placeholderArr = @[placeholderModel];
[dataList addObject:placeholderArr];
[selectIndexs addObject:@(0)];
}
}
}
self.dataList = [dataList copy];
self.selectIndexs = [selectIndexs copy];
}
}
#pragma mark - 选择器
- (UIPickerView *)pickerView {
if (!_pickerView) {
CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
_pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight)];
_pickerView.backgroundColor = self.pickerStyle.pickerColor;
_pickerView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
_pickerView.dataSource = self;
_pickerView.delegate = self;
}
return _pickerView;
}
#pragma mark - UIPickerViewDataSource
// 1.返回组件数量
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
switch (self.pickerMode) {
case BRTextPickerComponentSingle:
return 1;
case BRTextPickerComponentMulti:
case BRTextPickerComponentCascade:
{
if (self.pickerStyle.columnSpacing > 0) {
return self.dataList.count * 2 - 1;
}
return self.dataList.count;
}
default:
break;
}
}
// 2.返回每个组件的行数
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
switch (self.pickerMode) {
case BRTextPickerComponentSingle:
return self.dataList.count;
case BRTextPickerComponentMulti:
case BRTextPickerComponentCascade:
{
if (self.pickerStyle.columnSpacing > 0) {
if (component % 2 == 1) {
return 1;
} else {
component = component / 2;
}
}
if (component < self.dataList.count) {
return [self.dataList[component] count];
}
return 0;
}
break;
default:
break;
}
}
#pragma mark - UIPickerViewDelegate
// 3.设置 pickerView 的显示内容
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view {
// 1.自定义 row 的内容视图
UILabel *label = (UILabel *)view;
if (!label) {
label = [[UILabel alloc]init];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.font = self.pickerStyle.pickerTextFont;
label.textColor = self.pickerStyle.pickerTextColor;
label.numberOfLines = self.pickerStyle.maxTextLines;
// 字体自适应属性
label.adjustsFontSizeToFitWidth = YES;
// 自适应最小字体缩放比例
label.minimumScaleFactor = 0.5f;
}
// 2.设置选择器中间选中行的样式
[self.pickerStyle setupPickerSelectRowStyle:pickerView titleForRow:row forComponent:component];
// 设置文本
if (self.pickerMode == BRTextPickerComponentSingle) {
id item = row < self.dataList.count ? self.dataList[row] : nil;
if (item && [item isKindOfClass:[BRTextModel class]]) {
BRTextModel *model = (BRTextModel *)item;
label.text = model.text;
} else {
label.text = item;
}
} else if (self.pickerMode == BRTextPickerComponentMulti || self.pickerMode == BRTextPickerComponentCascade) {
// 如果有设置列间距,且是第奇数列,则不显示内容(即空白间隔列)
if (self.pickerStyle.columnSpacing > 0) {
if (component % 2 == 1) {
label.text = @"";
return label;
} else {
component = component / 2;
}
}
if (component < self.dataList.count) {
NSArray *itemArr = self.dataList[component];
id item = row < itemArr.count ? itemArr[row] : nil;
if (item && [item isKindOfClass:[BRTextModel class]]) {
BRTextModel *model = (BRTextModel *)item;
label.text = model.text;
} else {
label.text = item;
}
}
}
return label;
}
// 获取选择器是否滚动中状态
- (BOOL)getRollingStatus:(UIView *)view {
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView *scrollView = (UIScrollView *)view;
if (scrollView.dragging || scrollView.decelerating) {
// 如果 UIPickerView 正在拖拽或正在减速返回YES
return YES;
}
}
for (UIView *subView in view.subviews) {
if ([self getRollingStatus:subView]) {
return YES;
}
}
return NO;
}
// 选择器是否正在滚动
- (BOOL)isRolling {
return [self getRollingStatus:self.pickerView];
}
// 4.滚动 pickerView 执行的回调方法
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
switch (self.pickerMode) {
case BRTextPickerComponentSingle:
{
self.selectIndex = row;
if (!_isSyncingRollingSelection) {
// 滚动选择时执行 singleChangeBlock
self.singleChangeBlock ? self.singleChangeBlock([self getSingleSelectModel], self.selectIndex): nil;
}
}
break;
case BRTextPickerComponentMulti:
{
// 处理选择器有设置列间距时,选择器的滚动问题
if (self.pickerStyle.columnSpacing > 0) {
if (component % 2 == 1) {
return;
} else {
component = component / 2;
}
}
NSMutableArray *mutableArr = [NSMutableArray arrayWithArray:self.selectIndexs ?: @[]];
while (mutableArr.count <= component) {
[mutableArr addObject:@(0)];
}
[mutableArr replaceObjectAtIndex:component withObject:@(row)];
self.selectIndexs = [mutableArr copy];
if (!_isSyncingRollingSelection) {
// 滚动选择时执行 multiChangeBlock
self.multiChangeBlock ? self.multiChangeBlock([self getMultiSelectModels], self.selectIndexs): nil;
}
}
break;
case BRTextPickerComponentCascade:
{
// 处理选择器有设置列间距时,选择器的滚动问题
if (self.pickerStyle.columnSpacing > 0) {
if (component % 2 == 1) {
return;
} else {
component = component / 2;
}
}
if (component < self.selectIndexs.count) {
NSMutableArray *selectIndexs = [[NSMutableArray alloc]init];
for (NSInteger i = 0; i < self.selectIndexs.count; i++) {
if (i < component) {
[selectIndexs addObject:self.selectIndexs[i]];
} else if (i == component) {
[selectIndexs addObject:@(row)];
} else {
[selectIndexs addObject:@(0)];
}
}
self.selectIndexs = [selectIndexs copy];
}
if (!_isSyncingRollingSelection) {
// 刷新选择器数据
[self reloadData];
// 滚动选择时执行 multiChangeBlock
self.multiChangeBlock ? self.multiChangeBlock([self getMultiSelectModels], self.selectIndexs): nil;
}
}
break;
default:
break;
}
}
#pragma mark - 获取【单列】选择器选择的值
- (BRTextModel *)getSingleSelectModel {
id item = self.selectIndex < self.dataList.count ? self.dataList[self.selectIndex] : nil;
if ([item isKindOfClass:[BRTextModel class]]) {
BRTextModel *model = (BRTextModel *)item;
model.index = self.selectIndex;
return model;
} else {
BRTextModel *model = [[BRTextModel alloc]init];
model.index = self.selectIndex;
model.text = item;
return model;
}
}
#pragma mark - 获取【多列】选择器选择的值
- (NSArray *)getMultiSelectModels {
NSMutableArray *modelArr = [[NSMutableArray alloc]init];
for (NSInteger i = 0; i < self.dataList.count; i++) {
NSInteger index = i < self.selectIndexs.count ? [self.selectIndexs[i] integerValue] : 0;
NSArray *dataArr = i < self.dataList.count ? self.dataList[i] : @[];
id item = index >= 0 && index < dataArr.count ? dataArr[index] : nil;
if ([item isKindOfClass:[BRTextModel class]]) {
BRTextModel *model = (BRTextModel *)item;
model.index = index;
[modelArr addObject:model];
} else {
BRTextModel *model = [[BRTextModel alloc]init];
model.index = index;
model.text = item;
[modelArr addObject:model];
}
}
return [modelArr copy];
}
// 设置行高
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
return self.pickerStyle.rowHeight;
}
// 设置列宽
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
if (self.pickerStyle.columnSpacing > 0 && component % 2 == 1) {
return self.pickerStyle.columnSpacing;
}
NSInteger columnCount = [self numberOfComponentsInPickerView:pickerView];
CGFloat columnWidth = self.pickerView.bounds.size.width / columnCount - 5;
if (self.pickerStyle.columnWidth > 0 && self.pickerStyle.columnWidth <= columnWidth) {
return self.pickerStyle.columnWidth;
}
return columnWidth;
}
#pragma mark - 重写父类方法
- (void)reloadData {
// 1.处理数据源
[self handlerPickerData];
// 2.刷新选择器
[self.pickerView reloadAllComponents];
// 3.滚动到选择的值
if (self.pickerMode == BRTextPickerComponentSingle) {
if (self.selectIndex >= 0 && self.selectIndex < self.dataList.count) {
[self.pickerView selectRow:self.selectIndex inComponent:0 animated:self.selectRowAnimated];
}
} else if (self.pickerMode == BRTextPickerComponentMulti || self.pickerMode == BRTextPickerComponentCascade) {
for (NSInteger i = 0; i < self.selectIndexs.count; i++) {
NSInteger component = self.pickerStyle.columnSpacing > 0 ? i * 2 : i;
NSInteger row = i < self.selectIndexs.count ? [self.selectIndexs[i] integerValue] : 0;
[self.pickerView selectRow:row inComponent:component animated:self.selectRowAnimated];
}
}
}
- (void)addPickerToView:(UIView *)view {
// 1.添加选择器
if (view) {
// 立即刷新容器视图 view 的布局(防止 view 使用自动布局时,选择器视图无法正常显示)
[view setNeedsLayout];
[view layoutIfNeeded];
self.frame = view.bounds;
CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
CGFloat pickerFooterViewHeight = self.pickerFooterView ? self.pickerFooterView.bounds.size.height : 0;
self.pickerView.frame = CGRectMake(0, pickerHeaderViewHeight, view.bounds.size.width, view.bounds.size.height - pickerHeaderViewHeight - pickerFooterViewHeight);
[self addSubview:self.pickerView];
} else {
// iOS16重新设置 pickerView 高度解决懒加载设置frame不生效问题
CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
self.pickerView.frame = CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight);
[self.alertView addSubview:self.pickerView];
}
// ③添加中间选择行的两条分割线
if (self.pickerStyle.clearPickerNewStyle) {
[self.pickerStyle addSeparatorLineView:self.pickerView];
}
// 2.绑定数据
[self reloadData];
__weak typeof(self) weakSelf = self;
// 点击确定按钮的回调点击确定按钮后执行这个block回调
self.doneBlock = ^{
if (!weakSelf) {
return;
}
if (weakSelf.isRolling) {
[weakSelf handleAutoSelectRollingRow];
}
// 点击确定,执行选择结果回调
if (weakSelf.pickerMode == BRTextPickerComponentSingle) {
weakSelf.singleResultBlock ? weakSelf.singleResultBlock([weakSelf getSingleSelectModel], weakSelf.selectIndex): nil;
} else if (weakSelf.pickerMode == BRTextPickerComponentMulti || weakSelf.pickerMode == BRTextPickerComponentCascade) {
weakSelf.multiResultBlock ? weakSelf.multiResultBlock([weakSelf getMultiSelectModels], weakSelf.selectIndexs): nil;
}
};
[super addPickerToView:view];
}
#pragma mark - 获取指定列当前选中行(边界修正)
- (NSInteger)clampedSelectedRowInComponent:(NSInteger)component {
NSInteger maxRow = [_pickerView numberOfRowsInComponent:component] - 1;
if (maxRow < 0) {
return NSNotFound;
}
NSInteger row = [_pickerView selectedRowInComponent:component];
return MAX(0, MIN(row, maxRow));
}
#pragma mark - 处理滚动未结束前自动选择行
- (void)handleAutoSelectRollingRow {
if (!_pickerView) {
return;
}
NSInteger componentCount = [_pickerView numberOfComponents];
if (componentCount <= 0) {
return;
}
if (self.pickerMode == BRTextPickerComponentCascade) {
NSInteger changedIndex = NSNotFound;
NSInteger changedRow = NSNotFound;
for (NSInteger component = 0; component < componentCount; component++) {
if (self.pickerStyle.columnSpacing > 0 && component % 2 == 1) {
continue;
}
NSInteger dataComponent = self.pickerStyle.columnSpacing > 0 ? component / 2 : component;
NSInteger row = [self clampedSelectedRowInComponent:component];
if (row == NSNotFound) {
continue;
}
NSInteger oldRow = dataComponent < self.selectIndexs.count ? [self.selectIndexs[dataComponent] integerValue] : 0;
if (row != oldRow) {
changedIndex = dataComponent;
changedRow = row;
break;
}
}
if (changedIndex != NSNotFound) {
NSMutableArray<NSNumber *> *selectIndexs = [NSMutableArray array];
for (NSInteger index = 0; index < self.selectIndexs.count; index++) {
if (index < changedIndex) {
[selectIndexs addObject:self.selectIndexs[index]];
} else if (index == changedIndex) {
[selectIndexs addObject:@(changedRow)];
} else {
[selectIndexs addObject:@(0)];
}
}
self.selectIndexs = [selectIndexs copy];
[self reloadData];
}
return;
}
_isSyncingRollingSelection = YES;
if (self.pickerMode == BRTextPickerComponentSingle) {
NSInteger row = [self clampedSelectedRowInComponent:0];
if (row != NSNotFound) {
[self pickerView:_pickerView didSelectRow:row inComponent:0];
}
} else {
for (NSInteger component = 0; component < componentCount; component++) {
NSInteger row = [self clampedSelectedRowInComponent:component];
if (row == NSNotFound) {
continue;
}
[self pickerView:_pickerView didSelectRow:row inComponent:component];
}
}
_isSyncingRollingSelection = NO;
}
#pragma mark - 重写父类方法
- (void)addSubViewToPicker:(UIView *)customView {
[self.pickerView addSubview:customView];
}
#pragma mark - 弹出选择器视图
- (void)show {
[self addPickerToView:nil];
}
#pragma mark - 关闭选择器视图
- (void)dismiss {
[self removePickerFromView:nil];
}
#pragma mark - setter 方法
- (void)setFileName:(NSString *)fileName {
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
if (filePath && filePath.length > 0) {
if ([fileName hasSuffix:@".plist"]) {
// 获取本地 plist文件 数据源
NSArray *dataArr = [[NSArray alloc] initWithContentsOfFile:filePath];
if (dataArr && dataArr.count > 0) {
self.dataSourceArr = dataArr;
}
} else if ([fileName hasSuffix:@".json"]) {
// 获取本地 JSON文件 数据源
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSArray *dataArr = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
if (dataArr && dataArr.count > 0) {
self.dataSourceArr = [NSArray br_modelArrayWithJson:dataArr mapper:nil];
}
}
}
}
@end