[英]UITableViewCell doesn't get deselected when swiping back quickly
I've now updated three of my apps to iOS 7, but in all three, despite them not sharing any code, I have the problem where if the user swipes to go back in the navigation controller (rather than tap the back button) quickly, the cell will remain in its selected state. 我现在已经将我的三个应用程序更新到了iOS 7,但是在这三个应用程序中,尽管它们没有共享任何代码,但我遇到的问题是,如果用户滑动以返回导航控制器(而不是快速点击后退按钮) ,细胞将保持其选定状态。
For the three apps, one uses custom cells created programmatically, another uses custom cells created in a storyboard and the third uses default cells in a very basic subclass of UITableView, also in a storyboard. 对于这三个应用程序,一个使用以编程方式创建的自定义单元格,另一个使用在故事板中创建的自定义单元格,第三个使用UITableView的一个非常基本的子类中的默认单元格,也在故事板中。 In all three cases, the cells don't deselect by themselves.
在所有三种情况下,细胞不会自行取消选择。 If the user swipes slowly, or hits the back button, they deselect as normal.
如果用户缓慢滑动或按下后退按钮,则会正常取消选择。
This is only happening in my iOS 7 apps, Apple's own apps and third party apps upgraded for iOS 7 all seem to be behaving normally (albeit with slight differences in how quickly the cells gets deselected). 这只发生在我的iOS 7应用程序中,Apple自己的应用程序和为iOS 7升级的第三方应用程序似乎都表现正常(虽然细胞取消选择的速度略有不同)。
There must be something I'm doing wrong, but I'm not sure what? 一定有什么我做错了,但我不确定是什么?
This worked best for me: 这对我来说效果最好:
- (void)viewDidLoad {
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];
}
I even got a much better unselect fading while I was swiping back slowly. 当我慢慢地向后滑动时,我甚至有一个更好的未选择性褪色。
I'm dealing with the same problem right now. 我现在正处理同样的问题。 The UICatalog -sample from Apple seems to bring the dirty solution.
来自Apple的UICatalog -sample似乎带来了肮脏的解决方案。
It really doesn't make me happy at all. 它真的不会让我开心。 As mentioned before it uses
[self.tableView deselectRowAtIndexPath:tableSelection animated:NO];
如前所述它使用
[self.tableView deselectRowAtIndexPath:tableSelection animated:NO];
to deselect the currently selected row. 取消选择当前选定的行。
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// this UIViewController is about to re-appear, make sure we remove the current selection in our table view
NSIndexPath *tableSelection = [self.tableView indexPathForSelectedRow];
[self.tableView deselectRowAtIndexPath:tableSelection animated:NO];
// some over view controller could have changed our nav bar tint color, so reset it here
self.navigationController.navigationBar.tintColor = [UIColor darkGrayColor];
}
I have to mention the sample code may not be 我不得不提一下示例代码
iOS 7 IOS 7
iOS 8 iOS 8
iOS 9 iOS 10-ready iOS 9
iOS 10准备好了
Something which really confuses me is the UITableViewController Class Reference : 真正令我困惑的是UITableViewController类参考 :
When the table view is about to appear the first time it's loaded, the table-view controller reloads the table view's data.
当表视图即将在第一次加载时出现时,表视图控制器会重新加载表视图的数据。 It also clears its selection (with or without animation, depending on the request) every time the table view is displayed.
每次显示表格视图时,它还会清除其选择(有或没有动画,具体取决于请求)。 The
UITableViewController
class implements this in the superclass methodviewWillAppear:
.UITableViewController
类在超类方法viewWillAppear:
实现它viewWillAppear:
You can disable this behavior by changing the value in theclearsSelectionOnViewWillAppear
property.您可以通过更改
clearsSelectionOnViewWillAppear
属性中的值来禁用此行为。
This is exactly the behavior I expect… but it does not seem to work. 这正是我期望的行为......但它似乎不起作用。 Neither for you nor for me.
既不适合你,也不适合我。 We really have to use the "dirty" solution and do it on our own.
我们真的必须使用“脏”的解决方案,并自己做。
Fabio's answer works well but doesn't give the right look if the user swipes just a little bit and then changes their mind. Fabio的答案效果很好,但如果用户只是稍微滑动然后改变主意就不会给出正确的效果。 In order to get that case right you need to save the selected index path and reset it when necessary.
为了使这种情况正确,您需要保存选定的索引路径并在必要时重置它。
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.savedSelectedIndexPath = nil;
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (self.savedSelectedIndexPath) {
[self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.savedSelectedIndexPath = self.tableView.indexPathForSelectedRow;
if (self.savedSelectedIndexPath) {
[self.tableView deselectRowAtIndexPath:self.savedSelectedIndexPath animated:YES];
}
}
If using a UITableViewController, make sure to disable the built-in clearing: 如果使用UITableViewController,请确保禁用内置清除:
self.clearsSelectionOnViewWillAppear = NO;
and add the property for savedSelectedIndexPath: 并为savedSelectedIndexPath添加属性:
@property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath;
If you need to do this in a few different classes it might make sense to split it out in a helper, for example like I did in this gist: https://gist.github.com/rhult/46ee6c4e8a862a8e66d4 如果您需要在几个不同的类中执行此操作,可能需要将其拆分为帮助程序,例如我在此要点中所做的: https : //gist.github.com/rhult/46ee6c4e8a862a8e66d4
This solution animates the row deselection along with the transition coordinator (for a user-driven VC dismiss) and re-applies the selection if the user cancels the transition. 此解决方案为行取消选择和转换协调器(用于用户驱动的VC解除)设置动画,并在用户取消转换时重新应用选择。 Adapted from a solution by Caleb Davenport in Swift.
改编自Ciftb Davenport在Swift中的解决方案。 Only tested on iOS 9. Tested as working with both user driven (swipe) transition and the old-style "Back" button tap.
仅在iOS 9上测试。经测试同时使用用户驱动(滑动)过渡和旧式“后退”按钮。
In the UITableViewController
subclass: 在
UITableViewController
子类中:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Workaround. clearsSelectionOnViewWillAppear is unreliable for user-driven (swipe) VC dismiss
NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow;
if (indexPath && self.transitionCoordinator) {
[self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[self.tableView deselectRowAtIndexPath:indexPath animated:animated];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
if ([context isCancelled]) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}];
}
}
After running into this myself today I found out that this apparently is a fairly well-known problem with UITableView, its support for interactive navigation transitions is slightly broken. 在我今天遇到这个问题之后,我发现这显然是UITableView的一个相当着名的问题,它对交互式导航过渡的支持略有破坏。 The folks behind Castro have posted an excellent analysis and solution to this: http://blog.supertop.co/post/80781694515/viewmightappear
卡斯特罗背后的人们已经发布了一个很好的分析和解决方案: http : //blog.supertop.co/post/80781694515/viewmightappear
I decided to use their solution which also considers cancelled transitions: 我决定使用他们的解决方案,也考虑取消转换:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSIndexPath *selectedRowIndexPath = [self.tableView indexPathForSelectedRow];
if (selectedRowIndexPath) {
[self.tableView deselectRowAtIndexPath:selectedRowIndexPath animated:YES];
[[self transitionCoordinator] notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) {
if ([context isCancelled]) {
[self.tableView selectRowAtIndexPath:selectedRowIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}];
}
}
You can try to set 你可以尝试设置
self.clearsSelectionOnViewWillAppear = YES;
in a UITableViewController or 在UITableViewController或
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:NO];
in viewWillAppear, maybe before calling [super viewWillAppear:animated]; 在viewWillAppear中,也许在调用[super viewWillAppear:animated]之前; If your UItableView is not inside an UITableViewController you must deselect the cells manually:
如果您的UItableView 不在 UITableViewController中,则必须手动取消选择单元格:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
I'm using 我正在使用
[tableView deselectRowAtIndexPath:indexPath animated:YES];
at the end of method 在方法结束时
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Like this: 像这样:
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//doing something according to selected cell...
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Simple Swift 3/4 Answer: 简单的Swift 3/4答案:
override func viewWillAppear(_ animated: Bool) {
if tableView.indexPathForSelectedRow != nil {
self.tableView.deselectRow(at: tableView.indexPathForSelectedRow! as IndexPath, animated: true)
}
}
Use 使用
[tableView deselectRowAtIndexPath:indexPath animated:YES];
code in 代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method
I've found a very simple solution to this problem that just makes the default behavior work as it should. 我找到了一个非常简单的解决方案,只是让默认行为正常工作。 I wasn't satisfied with the solutions involving
deselectRowAtIndexPath
since the resulting visual effect was slightly different. 我对
deselectRowAtIndexPath
的解决方案不满意,因为产生的视觉效果略有不同。
All you have to do in order to prevent this weird behavior is to reload the table when the view is displayed: 为了防止这种奇怪的行为,你所要做的就是在显示视图时重新加载表:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.tableView reloadData];
}
Codestage answer , in Swift 3. notifyWhenInteractionEnds
is deprecated. Codestage答案 ,在Swift 3中。不推荐使用
notifyWhenInteractionEnds
。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
if let indexPath = self.tableView.indexPathForSelectedRow {
self.tableView.deselectRow(at: indexPath, animated: true)
self.transitionCoordinator?.notifyWhenInteractionChanges { (context) in
if context.isCancelled {
self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
}
}
}
}
You are probably not calling the super view's viewWillAppear method ([super viewWillAppear:animated];). 您可能没有调用超级视图的viewWillAppear方法([super viewWillAppear:animated];)。 When you do this and the UITableViewController's parameter clearsSelectionOnViewWillAppear is YES then the cells will be deselected on viewWillAppear.
执行此操作并且UITableViewController的参数clearsSelectionOnViewWillAppear为YES时,将在viewWillAppear上取消选择单元格。
Based on Rhult 's code, I made a few changes. 根据Rhult的代码,我做了一些改动。
This implementation allow user to cancel swipe back and still keep selected for future swipe back deselect animation 此实现允许用户取消向后滑动并仍然保持选定状态以供将来滑动取消选择动画
@property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath;
- (void)viewDidLoad {
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
}
-(void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.savedSelectedIndexPath = nil;
}
-(void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.savedSelectedIndexPath && ![self.tableView indexPathForSelectedRow]) {
[self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
} else {
self.savedSelectedIndexPath = [self.tableView indexPathForSelectedRow];
}
}
Rhult's solution works perfectly on iOS 9.2. Rhult的解决方案在iOS 9.2上完美运行。 This is the implementation in Swift:
这是Swift中的实现:
Declare a variable in your MasterViewController
to save the IndexPath: 在
MasterViewController
声明一个变量来保存IndexPath:
var savedSelectedIndexPath: NSIndexPath?
Then you can put the code in an extension for clarity: 然后,为了清楚起见,您可以将代码放在扩展名中:
extension MasterViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.savedSelectedIndexPath = nil
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if let indexPath = self.savedSelectedIndexPath {
self.tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.savedSelectedIndexPath = tableView.indexPathForSelectedRow
if let indexPath = self.savedSelectedIndexPath {
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
}
Codestage provided by far the best looking answer , so I decided to convert it into Swift 2. Codestage提供了迄今为止最好看的答案 ,因此我决定将其转换为Swift 2。
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
let selectedRowIndexPath = self.tableView.indexPathForSelectedRow
if ((selectedRowIndexPath) != nil) {
self.tableView.deselectRowAtIndexPath(selectedRowIndexPath!, animated: true)
self.transitionCoordinator()?.notifyWhenInteractionEndsUsingBlock({ context in
if (context.isCancelled()) {
self.tableView.selectRowAtIndexPath(selectedRowIndexPath, animated: false, scrollPosition: UITableViewScrollPosition.None)
}
})
}
}
For swift 对于迅捷
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
guard let indexPath = tableView.indexPathForSelectedRow else{
return
}
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.