如何使单元格中的 UIButton 在被点击后立即保持特定状态?

Posted

技术标签:

【中文标题】如何使单元格中的 UIButton 在被点击后立即保持特定状态?【英文标题】:How do I make UIButton in a cell retain a specific state right after being tapped? 【发布时间】:2014-04-26 11:50:28 【问题描述】:

所以我在UICollectionView 中有一个项目列表,并且有一个UIButton 是默认设置并选择UIImage 设置为浅灰色心形和黑色心形。

我的目标是允许用户将项目添加到收藏夹列表中。我在数据库中添加了一个名为“favourite”的列,它是布尔类型。

客户点击项目单元格中的心脏。触发一个方法,该方法使用作为 UIButton 的 titleLabel 传递的项目的对象 ID 从数据库中获取项目并将收藏列的布尔值更新为“True”。

收藏按钮方法:

- (void)addFavouriteButtonTapped:(UIButton *)sender

    NSLog(@"add to favourites button tapped %@", [[sender titleLabel] text]);

    // Set data base favaourite status to 1 (yes)
    PFQuery *query = [PFQuery queryWithClassName:@"Garments"];
    [query getObjectInBackgroundWithId:[[sender titleLabel] text] block:^(PFObject *object, NSError *error) 
        object[@"favourite"] = @YES;
        [object saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) 
            if (!error) 
                [[[UIAlertView alloc] initWithTitle:@"Add Favouite"
                                            message:@"Item successfully added to favourites"
                                           delegate:_thisController
                                  cancelButtonTitle:@"Ok"
                                  otherButtonTitles: nil] show];
             else 
                NSLog(@"Something went wrong");
            
        ]; 
    ];   

然后在我的cellForItemAtIndexPath 中,我有一个 if 语句来设置UIButton 的选定状态。

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object


        NSLog(@"cellForItemAtIndexPath");
        // other cell info here deleted to make things clearer

         _addFavouriteButton = [cell addFavouriteButton];


        // if database favourites status is equal to one then set highlighted to (yes)
        if ([object valueForKey:@"favourite"] ==  [NSNumber numberWithBool:YES]) 
            [_addFavouriteButton setSelected:YES];
            NSLog(@"SETTING SELECTED");
         else if ([object valueForKey:@"favourite"] ==  [NSNumber numberWithBool:NO]) 
            [_addFavouriteButton setSelected:NO];
        

        [[_addFavouriteButton titleLabel] setText:[object objectId]];

        [_addFavouriteButton addTarget:_thisController action:@selector(addFavouriteButtonTapped:) forControlEvents:UIControlEventTouchUpInside];

        return cell;

现在,当我离开视图并且一切都被破坏然后再次返回时,黑色的心出现在我选择最喜欢的地方。这可以。我遇到的问题是,当我实际选择要添加为收藏的项目时,心脏会更新并保持灰色,直到我离开 UICollectionView 并返回。

我在 addFavouriteButtonTapped: 方法的主队列中尝试了reloadData 我也试过只重新加载单元格。

这些都不起作用。

我可以将UIButton 的状态设置为选中,但是当我滚动时,选择不会保持位置并移动到其他单元格UIButton 并使用我实际选择的按钮离开单元格。解决这个问题的唯一方法是保留少数几个,这样我们就有了一个新的UICollectionView,它使用我数据库中的 if 语句来确定是否选择了一个单元格。

**My custom cell:**

@interface VAGGarmentCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet PFImageView *imageView;
@property (weak, nonatomic) IBOutlet UIButton *addFavouriteButton;

@property (weak, nonatomic) IBOutlet UITextView *title;
@property (weak, nonatomic) IBOutlet UILabel *price;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;

@end

问题:

我已经完成了大部分需要完成的工作,以后可以在构建列出客户收藏列表中的项目的页面时使用收藏列。但是如何在点击按钮并成功更新后立即刷新UICollectionView 以显示UIButton 的选定状态?

【问题讨论】:

在您的单元格中设置索引路径 _addFavouriteButton.tag=indexpath.row 处的行 【参考方案1】:

确保您还将sender(即按下的按钮)也设置为选中,或者在更新模型后重新加载集合视图。

更新:此答案已在聊天讨论后进行编辑和更新。

在您的视图控制器中,_addFavouriteButton 不需要 iVar(或 @property),按钮变量应该只是 collectionView:cellForItemAtIndexPath:object: 方法范围内的局部变量 - 让我们开始吧通过首先修复该方法

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object

    NSLog(@"cellForItemAtIndexPath");
    // other cell info here deleted to make things clearer

    // Create a local variable for your button - this saves a bit of typing in the rest
    // of the method. You can also use dot notation as its a property
    UIButton *favouriteButton = cell.addFavouriteButton;

    BOOL objectFavourited = [object[@"favourite"] boolValue];
    NSLog(@"Setting selected? %@", objectFavourited ? @"YES" : @"NO");
    favouriteButton.selected = objectFavourited;

    // You should set the title directly on the button (as per the docs - 
    // https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIButton_Class/UIButton/UIButton.html#//apple_ref/occ/instp/UIButton/titleLabel), 
    // you shouldn't touch titleLabel except for styling

    // However you only used the title for seeing which object the favourite button tap was for,
    // so I've commented it out
    // [favouriteButton setTitle:[object objectId] forState:UIControlStateNormal];

    [favouriteButton addTarget:_thisController action:@selector(addFavouriteButtonTapped:) forControlEvents:UIControlEventTouchUpInside];

    return cell;

现在是按钮目标方法。在这里您应该执行以下操作:更新您的模型,更改按钮状态,然后保存模型。如果模型成功保存,您的用户是否关心 - 不是真的!但是,如果保存失败,则用户需要知道,并且按钮和模型需要恢复到之前的状态。 我很确定如果保存失败,对象将处于与您尝试保存之前相同的状态——favourite 属性设置为YES。即使没有,将其设置回NO 也无所谓。

获得按下按钮的项目的indexPath 的最佳方法是使用与this answer 中的方法类似的方法。它是计算索引路径的完美方式,不需要可怕的标签!

现在我们有了项目的索引路径,你不需要查询那个特定的对象——我们直接使用objectAtIndexPath:得到它。

- (void)addFavouriteButtonTapped:(UIButton *)button

    // Method adapted for a collection view from the above answer
    CGPoint hitPoint = [button convertPoint:CGPointZero toView:self.collectionView]; 
    NSIndexPath *hitIndex = [self.collectionView indexPathForItemAtPoint:hitPoint];

    // Now get the object for this index
    PFObject *object = [self objectAtIndexPath:hitIndex];

    NSLog(@"add to favourites button tapped %ld - %@", (long)hitIndex.item, [object objectId]); 

    // Now update the model
    object[@"favourite"] = @YES;

    // And the button state
    button.selected = YES;

    // Now save the model to parse
    [object saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) 

        if (!succeeded) 
            // Reset the object back to its previous state
            object[@"favourite"] = @NO;
            // Reload the item to reflect the changes in the model
            [self.collectionView reloadItemsAtIndexPaths:@[hitIndex]];

            // Only bother the user if something bad happened
            [[[UIAlertView alloc] initWithTitle:@"Oops!"
                                        message:@"There was a problem please try again later"
                                       delegate:self
                              cancelButtonTitle:@"Dismiss"
                              otherButtonTitles:nil] show];
        
    ];

您可以重新加载集合视图,但由于您已使用该方法更新了视图,因此实际上没有任何必要。 但是,如果保存失败,您应该重新加载集合视图,因为该按钮现在可能正在用于集合视图中的其他项目!

【讨论】:

这是我尝试的第一件事,但是当我滚动然后返回单元格时,我选择了它上面的按钮不再被选中。 问题是在查询完成之前你没有做任何UI明智的事情,所以在请求完成之前不会选择你的按钮。实际上,我会在我的回答中注明:) 那么在 _addFavoute 方法的顶部添加 hitPoint 代码?然后在索引路径成功更新重新加载项目后?我已经用你的代码试过了,它对我不起作用。 由于某种原因,重新加载似乎不起作用。我什至试过 [_collectionView reloadItemsAtIndexPaths:[_collectionView indexPathsForVisibleItems]]; @LondonGuy 是的,在 addFavouriteButtonTapped: 方法的顶部。实际上你可以发布更多代码,比如object是什么?它应该是一个数组,但它似乎不是?

以上是关于如何使单元格中的 UIButton 在被点击后立即保持特定状态?的主要内容,如果未能解决你的问题,请参考以下文章

如何使 UIButton 仅出现在最后一个单元格中?

如何使自定义单元格中的 UILabel 显示保存在 UITableView 中的更改

如何从模态 UIView 更新自定义单元格中的 UIButton 标题

重用 tableview 单元格中的元素

UITableViewCell didSelectRowAt:按下单元格中的 UIButton 区域时未调用

修改 UICollectionView 中特定单元格中的 UIButton