From 09e82e1e5e280aec700c51621284fd770c80ff71 Mon Sep 17 00:00:00 2001 From: renbo Date: Fri, 26 Apr 2024 19:12:16 +0800 Subject: [PATCH] fix #304 #305 --- .../AddressPickerView/BRAddressPickerView.h | 3 + .../AddressPickerView/BRAddressPickerView.m | 50 +++++++++++++++- .../DatePickerView/BRDatePickerView.h | 3 + .../DatePickerView/BRDatePickerView.m | 58 +++++++++++++++++-- .../StringPickerView/BRStringPickerView.h | 3 + .../StringPickerView/BRStringPickerView.m | 50 +++++++++++++++- .../Demo/Controller/TestViewController.m | 13 +---- .../BRMutableDatePickerView.m | 2 +- 8 files changed, 159 insertions(+), 23 deletions(-) diff --git a/BRPickerView/AddressPickerView/BRAddressPickerView.h b/BRPickerView/AddressPickerView/BRAddressPickerView.h index 4e07bc5..431e15d 100755 --- a/BRPickerView/AddressPickerView/BRAddressPickerView.h +++ b/BRPickerView/AddressPickerView/BRAddressPickerView.h @@ -48,6 +48,9 @@ typedef void(^BRAddressResultBlock)(BRProvinceModel * _Nullable province, BRCity /** 滚动选择时触发的回调 */ @property (nullable, nonatomic, copy) BRAddressResultBlock changeBlock; +/** 判断选择器是否处于滚动中。可以用于解决快速滑动选择器,在滚动结束前秒选确定按钮,出现显示不对的问题。*/ +@property (nonatomic, readonly, assign, getter=isRolling) BOOL rolling; + /** * 地区数据源(不传或为nil,默认就获取本地 BRCity.json 文件的数据) * 1.可以传 JSON数组,要注意 层级结构 和 key 要与 BRCity.json 保持一致 diff --git a/BRPickerView/AddressPickerView/BRAddressPickerView.m b/BRPickerView/AddressPickerView/BRAddressPickerView.m index cb2166b..7407d3d 100755 --- a/BRPickerView/AddressPickerView/BRAddressPickerView.m +++ b/BRPickerView/AddressPickerView/BRAddressPickerView.m @@ -32,6 +32,10 @@ @interface BRAddressPickerView () // 记录区选中的位置 @property(nonatomic, assign) NSInteger areaIndex; +// 记录滚动中的位置 +@property(nonatomic, assign) NSInteger rollingComponent; +@property(nonatomic, assign) NSInteger rollingRow; + @property (nonatomic, copy) NSArray * mSelectValues; @end @@ -256,7 +260,7 @@ - (UIPickerView *)pickerView { } #pragma mark - UIPickerViewDataSource -// 1.设置 pickerView 的列数 +// 1.返回组件数量 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { switch (self.pickerMode) { case BRAddressPickerModeProvince: @@ -274,7 +278,7 @@ - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { } } -// 2.设置 pickerView 每列的行数 +// 2.返回每个组件的行数 - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { if (component == 0) { // 返回省个数 @@ -321,9 +325,41 @@ - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forC // 2.设置选择器中间选中行的样式 [self.pickerStyle setupPickerSelectRowStyle:pickerView titleForRow:row forComponent:component]; + // 3.记录选择器滚动过程中选中的列和行 + // 获取选择器组件滚动中选中的行 + NSInteger selectRow = [pickerView selectedRowInComponent:component]; + if (selectRow >= 0) { + self.rollingComponent = component; + self.rollingRow = selectRow; + } + 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 { if (component == 0) { // 选择省 @@ -461,8 +497,16 @@ - (void)addPickerToView:(UIView *)view { [self reloadData]; __weak typeof(self) weakSelf = self; + // 点击确定按钮的回调:点击确定按钮后,执行这个block回调 self.doneBlock = ^{ - // 点击确定按钮后,执行block回调 + if (weakSelf.isRolling) { + NSLog(@"选择器滚动还未结束"); + // 问题:如果滚动选择器过快,然后在滚动过程中快速点击确定按钮,会导致 didSelectRow 代理方法还没有执行,出现没有选中的情况。 + // 解决:这里手动处理一下,如果滚动还未结束,强制执行一次 didSelectRow 代理方法,选择当前滚动的行。 + [weakSelf pickerView:weakSelf.pickerView didSelectRow:weakSelf.rollingRow inComponent:weakSelf.rollingComponent]; + } + + // 执行选择结果回调 if (weakSelf.resultBlock) { weakSelf.resultBlock(weakSelf.selectProvinceModel, weakSelf.selectCityModel, weakSelf.selectAreaModel); } diff --git a/BRPickerView/DatePickerView/BRDatePickerView.h b/BRPickerView/DatePickerView/BRDatePickerView.h index 314f780..b4f9121 100755 --- a/BRPickerView/DatePickerView/BRDatePickerView.h +++ b/BRPickerView/DatePickerView/BRDatePickerView.h @@ -102,6 +102,9 @@ typedef void (^BRDateResultRangeBlock)(NSDate * _Nullable selectStartDate, NSDat /** 滚动选择范围时触发的回调:for `BRDatePickerModeYQ`、`BRDatePickerModeYMW`、`BRDatePickerModeYW`, ignored otherwise. */ @property (nullable, nonatomic, copy) BRDateResultRangeBlock changeRangeBlock; +/** 判断选择器是否处于滚动中。可以用于解决快速滑动选择器,在滚动结束前秒选确定按钮,出现显示不对的问题。*/ +@property (nonatomic, readonly, assign, getter=isRolling) BOOL rolling; + /** 日期单位显示类型 */ @property (nonatomic, assign) BRShowUnitType showUnitType; diff --git a/BRPickerView/DatePickerView/BRDatePickerView.m b/BRPickerView/DatePickerView/BRDatePickerView.m index 6d38a0b..e3c5c6b 100755 --- a/BRPickerView/DatePickerView/BRDatePickerView.m +++ b/BRPickerView/DatePickerView/BRDatePickerView.m @@ -54,6 +54,10 @@ @interface BRDatePickerView () @property(nonatomic, assign) NSInteger yearWeekIndex; @property(nonatomic, assign) NSInteger quarterIndex; +// 记录滚动中的位置 +@property(nonatomic, assign) NSInteger rollingComponent; +@property(nonatomic, assign) NSInteger rollingRow; + // 记录选择的值 @property (nonatomic, strong) NSDate *mSelectDate; @property (nonatomic, copy) NSString *mSelectValue; @@ -757,7 +761,7 @@ - (UIPickerView *)pickerView { } #pragma mark - UIPickerViewDataSource -// 1.设置 pickerView 的列数 +// 1.返回组件数量 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { if (self.pickerMode == BRDatePickerModeYMDHMS) { return 6; @@ -791,7 +795,7 @@ - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 0; } -// 2.设置 pickerView 每列的行数 +// 2.返回每个组件的行数 - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { NSArray *rowsArr = [NSArray array]; if (self.pickerMode == BRDatePickerModeYMDHMS) { @@ -857,10 +861,19 @@ - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forC // 2.设置选择器中间选中行的样式 [self.pickerStyle setupPickerSelectRowStyle:pickerView titleForRow:row forComponent:component]; + + // 3.记录选择器滚动过程中选中的列和行 + // 获取选择器组件滚动中选中的行 + NSInteger selectRow = [pickerView selectedRowInComponent:component]; + if (selectRow >= 0) { + self.rollingComponent = component; + self.rollingRow = selectRow; + } return label; } +// 返回每行的标题 - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { NSString *titleString = @""; if (self.pickerMode == BRDatePickerModeYMDHMS) { @@ -978,7 +991,36 @@ - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row f return titleString; } -// 4.滚动 pickerView 执行的回调方法 +// 获取选择器是否滚动中状态 +- (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 { + if (self.style == BRDatePickerStyleSystem) { + return [self getRollingStatus:self.datePicker]; + } else if (self.style == BRDatePickerStyleCustom) { + return [self getRollingStatus:self.pickerView]; + } + return NO; +} + +// 4.滚动 pickerView 完成,执行的回调方法 - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { NSString *lastSelectValue = self.mSelectValue; NSDate *lastSelectDate = self.mSelectDate; @@ -1626,8 +1668,16 @@ - (void)addPickerToView:(UIView *)view { [self reloadData]; __weak typeof(self) weakSelf = self; + // 点击确定按钮的回调:点击确定按钮后,执行这个block回调 self.doneBlock = ^{ - // 点击确定按钮后,执行block回调 + if (weakSelf.isRolling) { + NSLog(@"选择器滚动还未结束"); + // 问题:如果滚动选择器过快,然后在滚动过程中快速点击确定按钮,会导致 didSelectRow 代理方法还没有执行,出现没有选中的情况。 + // 解决:这里手动处理一下,如果滚动还未结束,强制执行一次 didSelectRow 代理方法,选择当前滚动的行。 + [weakSelf pickerView:weakSelf.pickerView didSelectRow:weakSelf.rollingRow inComponent:weakSelf.rollingComponent]; + } + + // 执行选择结果回调 if (weakSelf.resultBlock) { weakSelf.resultBlock(weakSelf.mSelectDate, weakSelf.mSelectValue); } diff --git a/BRPickerView/StringPickerView/BRStringPickerView.h b/BRPickerView/StringPickerView/BRStringPickerView.h index 9f373e2..399a285 100755 --- a/BRPickerView/StringPickerView/BRStringPickerView.h +++ b/BRPickerView/StringPickerView/BRStringPickerView.h @@ -77,6 +77,9 @@ typedef void(^BRStringResultModelArrayBlock)(NSArray * _Nullab /** 滚动选择时触发的回调【多列】 */ @property (nullable, nonatomic, copy) BRStringResultModelArrayBlock changeModelArrayBlock; +/** 判断选择器是否处于滚动中。可以用于解决快速滑动选择器,在滚动结束前秒选确定按钮,出现显示不对的问题。*/ +@property (nonatomic, readonly, assign, getter=isRolling) BOOL rolling; + /** * 最大层级数(列数) for `BRStringPickerComponentLinkage`, ignored otherwise. * 使用场景:默认可选,当数据源中有 key 等于 parentKey 情况时,必须要设置 diff --git a/BRPickerView/StringPickerView/BRStringPickerView.m b/BRPickerView/StringPickerView/BRStringPickerView.m index 334cefd..4baaa30 100755 --- a/BRPickerView/StringPickerView/BRStringPickerView.m +++ b/BRPickerView/StringPickerView/BRStringPickerView.m @@ -20,6 +20,10 @@ @interface BRStringPickerView () /** 多列选择的值 */ @property (nonatomic, copy) NSArray * mSelectValues; +// 记录滚动中的位置 +@property(nonatomic, assign) NSInteger rollingComponent; +@property(nonatomic, assign) NSInteger rollingRow; + /** 数据源 */ @property (nullable, nonatomic, copy) NSArray *mDataSourceArr; @@ -249,7 +253,7 @@ - (UIPickerView *)pickerView { } #pragma mark - UIPickerViewDataSource -// 1.设置 pickerView 的列数 +// 1.返回组件数量 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { switch (self.pickerMode) { case BRStringPickerComponentSingle: @@ -265,7 +269,7 @@ - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { } } -// 2.设置 pickerView 每列的行数 +// 2.返回每个组件的行数 - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { switch (self.pickerMode) { case BRStringPickerComponentSingle: @@ -322,9 +326,41 @@ - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forC // 2.设置选择器中间选中行的样式 [self.pickerStyle setupPickerSelectRowStyle:pickerView titleForRow:row forComponent:component]; + // 3.记录选择器滚动过程中选中的列和行 + // 获取选择器组件滚动中选中的行 + NSInteger selectRow = [pickerView selectedRowInComponent:component]; + if (selectRow >= 0) { + self.rollingComponent = component; + self.rollingRow = selectRow; + } + 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) { @@ -492,8 +528,16 @@ - (void)addPickerToView:(UIView *)view { [self reloadData]; __weak typeof(self) weakSelf = self; + // 点击确定按钮的回调:点击确定按钮后,执行这个block回调 self.doneBlock = ^{ - // 点击确定按钮后,执行block回调 + if (weakSelf.isRolling) { + NSLog(@"选择器滚动还未结束"); + // 问题:如果滚动选择器过快,然后在滚动过程中快速点击确定按钮,会导致 didSelectRow 代理方法还没有执行,出现没有选中的情况。 + // 解决:这里手动处理一下,如果滚动还未结束,强制执行一次 didSelectRow 代理方法,选择当前滚动的行。 + [weakSelf pickerView:weakSelf.pickerView didSelectRow:weakSelf.rollingRow inComponent:weakSelf.rollingComponent]; + } + + // 执行选择结果回调 if (weakSelf.pickerMode == BRStringPickerComponentSingle) { if (weakSelf.resultModelBlock) { weakSelf.resultModelBlock([weakSelf getResultModel]); diff --git a/BRPickerViewDemo/Classes/Demo/Controller/TestViewController.m b/BRPickerViewDemo/Classes/Demo/Controller/TestViewController.m index 7f06c98..f263495 100755 --- a/BRPickerViewDemo/Classes/Demo/Controller/TestViewController.m +++ b/BRPickerViewDemo/Classes/Demo/Controller/TestViewController.m @@ -318,11 +318,9 @@ - (void)handlerTextFieldSelect:(UITextField *)textField { self.birthdaySelectDate = selectDate; self.infoModel.birthdayStr = selectValue; textField.text = selectValue; - NSLog(@"selectValue=%@", selectValue); NSLog(@"selectDate=%@", selectDate); NSLog(@"---------------------------------"); - }; datePickerView.resultRangeBlock = ^(NSDate * _Nullable selectStartDate, NSDate * _Nullable selectEndDate, NSString * _Nullable selectValue) { @@ -713,7 +711,7 @@ - (UIView *)footerView { _footerView.autoresizingMask = UIViewAutoresizingFlexibleWidth; // 1.切换日期选择器的显示模式 - UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:@[@"年月日时", @"年月日", @"年月", @"月周", @"年周", @"季 度"]]; + UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:@[@"年月日时", @"年月日", @"年月"]]; segmentedControl.frame = CGRectMake(40, 50, self.view.bounds.size.width - 80, 36); segmentedControl.autoresizingMask = UIViewAutoresizingFlexibleWidth; segmentedControl.apportionsSegmentWidthsByContent = YES; @@ -838,15 +836,6 @@ - (void)pickerModeSegmentedControlAction:(UISegmentedControl *)sender { } else if (selecIndex == 2) { NSLog(@"年月"); self.datePickerView.pickerMode = BRDatePickerModeYM; - } else if (selecIndex == 3) { - NSLog(@"年月"); - self.datePickerView.pickerMode = BRDatePickerModeYMW; - } else if (selecIndex == 4) { - NSLog(@"年月"); - self.datePickerView.pickerMode = BRDatePickerModeYW; - } else if (selecIndex == 5) { - NSLog(@"年月"); - self.datePickerView.pickerMode = BRDatePickerModeYQ; } // 重置选择的值 diff --git a/BRPickerViewDemo/Classes/Other/BRMutableDatePickerView/BRMutableDatePickerView.m b/BRPickerViewDemo/Classes/Other/BRMutableDatePickerView/BRMutableDatePickerView.m index 370658e..f48d1a8 100644 --- a/BRPickerViewDemo/Classes/Other/BRMutableDatePickerView/BRMutableDatePickerView.m +++ b/BRPickerViewDemo/Classes/Other/BRMutableDatePickerView/BRMutableDatePickerView.m @@ -569,7 +569,7 @@ - (void)scrollToSelectDate:(NSDate *)selectDate animated:(BOOL)animated { } #pragma mark - UIPickerViewDataSource -// 1. 设置 picker 的列数 +// 1.返回组件数量 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 3; }