滚动时读取 UIPickerView 的位置?

Posted

技术标签:

【中文标题】滚动时读取 UIPickerView 的位置?【英文标题】:Read position of UIPickerView while scrolling? 【发布时间】:2011-03-08 20:06:48 【问题描述】:

我有一个带有自定义单元格的 UIPickerView。每时每刻,我都想知道哪个单元格位于中心位置(在有色选择指示器后面)。 UIPickerView 委托方法

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component

确实提供此信息,但仅在选择器视图停止后提供。我正在寻找的是一种在选择器视图仍在旋转且尚未停止时检索当前“选定”单元格的方法。

作为一种解决方法,我尝试通过 KVO 并注册每个单元格的视图更改:

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view 
    .
    .
    [cell addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

我确实收到了对observeValueForKeyPath:ofObject:change:context: 的回调,但是无论我向上还是向下滚动选择器视图,收到的值似乎总是相同的:

2011-03-09 08:00:34.429 Project[35069:207] Frame: x=2.00, y=-13.00
2011-03-09 08:00:34.430 Project[35069:207] Location: x=4.00, y=-26.00
2011-03-09 08:00:34.430 Project[35069:207] Change: 
    kind = 1;
    new = "NSRect: 2, -13, 118, 70";
    old = "NSRect: 2, -13, 118, 70";

2011-03-09 08:00:34.431 Project[35069:207] Frame: x=2.00, y=-13.00
2011-03-09 08:00:34.431 Project[35069:207] Location: x=4.00, y=-26.00
2011-03-09 08:00:34.432 Project[35069:207] Change: 
    kind = 1;
    new = "NSRect: 2, -13, 118, 70";
    old = "NSRect: 2, -13, 118, 70";

(Location coordinates are in window frame) 

还有什么我可以尝试获得该价值的想法吗?

【问题讨论】:

【参考方案1】:

我认为的方法:

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view`

与:

[pickerView selectedRowInComponent:0]);

正是你想要的。

【讨论】:

【参考方案2】:

您可以使用您的选择器委托的-pickerView:titleForRow:forComponent: 方法让您很好地了解选择器的***在哪里。当您向一个方向或另一个方向滚动时,选择器将向其代表询问一行的标题(或视图)。***“中心”与选择器要求的行之间的行数取决于行高,但您可能知道那是什么。

您必须进行一些试验才能做到正确。选择器似乎要求一两行尚不可见,也许是为了保持滚动流畅。在我使用显示 5 行的选择器的测试中,选择器要求的行与中心之间似乎存在 3 行差异。您显然还必须跟踪旋转方向(向上或向下),并且在调用 -pickerView:didSelectRow:inComponent: 之前,您不会知道是选择了前两行还是最后两行或三行。

【讨论】:

我在其他地方看到过这个想法,但似乎有点不安全。您永远不知道将准确预加载多少视图。该数字可能在不同的 ios 版本中有所不同,也可能取决于设备的内存可用性。我查阅了文档,但找不到这方面的任何规定。 绝对不能保证,正如我上面提到的,该技术并非没有缺陷。我绝对建议进行彻底的测试。不过,除了滚动您自己的选择器视图之外,我认为这是您最接近实时通知选择器更改的方式。 我这样做了,在这里我检查每个更改是否是连续的,因为 pickerView:titleForRow:forComponent: 以随机方式预取行,所以我将旧行保留在某处并仅在它是一上一下:~if (row == self.oldrow + 1 || row == self.oldrow - 1)~【参考方案3】:

我觉得这很有趣。我想用 UIPicker 做类似的事情,但还没有深入研究。

UIDatePicker 实际上是这样做的,您可以在中午左右拖动时间,即使手指尚未从屏幕上抬起,它也会切换 AM/PM(它不应该称为“didSelectRow”)。

UIDatePicker 实际上是 UIControl 的子类,而不是 UIPickerView 之类的 UIView。因此,UIDatePicker 可能正在观察所有 UIControlEvents 来执行此操作。

观察 -pickerView:titleForRow:forComponent 现在可能有效,但是如果以后行为发生变化(即缓存行为,即)发生变化,这将中断...

一个建议是在中心属性上尝试 KVO,而不是框架。

Edit1:顺便问一下,你的 observeValueForKeyPath 方法怎么样?

Edit2:经过进一步研究,似乎大部分 UIKit 都不符合 KVO。 看到这个答案 - Key value Observing during UIView Animations

因此,似乎唯一的选择是在视图可见时继续对视图的值进行轮询。无论何时,forloop 和杆框架/中心属性如何?

【讨论】:

【参考方案4】:

框架没有改变,因为单元格没有移动。它的超级视图之一是,并且单元格与它一起携带。要计算父单元UIPickerView 中的单元格坐标,请尝试以下操作:

CGRect theApparentRect = [theCell convertRect: theCell.bounds toView: thePicker];

生成的矩形将在thePicker的坐标系中,然后您可以通过以下简单测试确定它是否在中间:

CGPoint middleOfThePicker = CGPointMake
(
    CGRectGetMidX(thePicker.bounds),
    CGRectGetMidY(thePicker.bounds)
);
Boolean isInMiddle = CGRectContainsPoint(theApparentRect, middleOfThePicker);

【讨论】:

我已经在使用框架的原点进行此操作。不要认为我改变边界或框架的原点有什么区别吗? 不,尽管单元格的原点可能与容器视图的可见边界对齐,也可能不对齐。试试完整的矩形看看?【参考方案5】:

只是一个快速的想法,但是您可以在 UIPickerView 上添加一个 UIScrollView(透明的,内容视图与组件的数量一样高。)。

然后监听 UIScrollViewDelegate 方法来获取你需要的实时滚动数据。

棘手的部分是将触摸事件从滚动条传递到选择器,以便它按预期运行。

【讨论】:

【参考方案6】:

UIPickerView 的问题是您为单元格设置的视图实际上是子视图。 UIPickerView 由各种 imageView、一个 TableView 和一个选择线构建而成。 您在使用 viewForRow 时提供的视图作为 tableView 单元格的子视图插入,因此它们的框架永远不会改变。 唯一改变的框架是实际 tableView 单元格的框架,您当然无法访问。

一种可能的解决方案是简单地将 UIPickerView 的 tableView 替换为您自己的。

您可以通过将 UIPickerView 的行数设置为 0 来实现,并在 UIPickerView 上放置一个透明的 tableView。您的 tableView 将保存实际的单元格,并且由于 UITableView 继承自 UIScrollView,因此您可以获取有关滚动的事件并简单地检查哪个 UITableViewCell 当前位于选择行下方。

【讨论】:

以上是关于滚动时读取 UIPickerView 的位置?的主要内容,如果未能解决你的问题,请参考以下文章

触摸 UITextField 时设置 UIPickerView 的默认值

UIPickerView iOS 11 滚动和选择错误

UIPickerView 在滚动时跳过数组中的值

使自定义 UI 元素不在 TableViewController 中滚动

UIPickerView 在重新加载时冻结 UI

滚动使用 IB 设置的 UIPickerView 时崩溃