Search code examples
iosobjective-cuitableviewuiviewanimationnslayoutconstraint

Animate constraint change in table cell


I have a slider in my table cell, and when the user slides it, I'm reloading that particular cell to show the updated slider value in my cell.

And when the user moved the slider to the max, i Want to change the width constraint of an element in my cell with some animation.

Everything works but I couldnt see the animation.

-(void)sliderValueChanged:(id)sender{

    sliderValueChanged = YES;
    UISlider *slider = (UISlider *)sender;

    int sliderValue;
    sliderValue = roundf(slider.value);
    [slider setValue:sliderValue animated:YES];

    NSIndexPath *indexPath = [self getIndexPathForView:slider];

    MySliderCell *sliderCell = (MySliderCell *)[self tableView:self.tableView cellForRowAtIndexPath:indexPath];

    sliderCell.sliderValueLabel.text = [NSString stringWithFormat:@"%d",sliderValue];

    if(sliderValue == 7){
        [sliderCell layoutIfNeeded];
        sliderCell.radioButonWidth.constant = 40;
        [UIView animateWithDuration:2.0
                         animations:^{
                             [sliderCell layoutIfNeeded];
                         }];
    }else{
        [sliderCell layoutIfNeeded];
        sliderCell.radioButonWidth.constant = 0;
        [UIView animateWithDuration:2.0
                         animations:^{
                             [sliderCell layoutIfNeeded];
                         }];

    }

    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

    DLog(@"slider value :%d change method called, indexpath is %@",sliderValue,indexPath);


}

I have also tried usig setNeedsUpdateConstraints, and calling layoutIfNeeded on self.tableView instead of cell. But nothing worked. What am I doing wrong ?


Solution

  • The problem in your example is that you call reloadRowsAtIndexPaths in the same loop as the animation block. Generally all you would need to do is remove that call and the animation should work.

    As from the comments it would seem that without calling it not even the text is changed in the cell which leads to the issue that you are probably not getting a correct cell when requesting it from the slider action method.

    I suggest you to always use all the outlets of the cell inside a subclass of the table view cell. This way you can always be sure you have access to the correct subviews without calling getIndexPathForView, cellForRowAtIndexPath and in some cases then getting an object from your data source array with that index path like myObject = self.array[indexPath.row]. By doing so the only downside is to get the notification from the cell back to the table view owner (usually the view controller). For that case you generally use custom delegates where the cell would have a weak reference to the delegate (the view controller) and call the delegate on any action needed. To give an example of such table view cell:

    Header:

    @class MyTableViewCell;
    
    @protocol MyTableViewCellDelegate <NSObject>
    
    - (void)myTableViewCell:(MyTableViewCell *)sender changedSliderValue:(CGFloat)newValue;
    
    @end
    
    @interface MyTableViewCell : UITableViewCell
    
    @property (nonatomic, weak) id<MyTableViewCellDelegate> delegate;
    @property (nonatomic, strong) MyCustomObject *myObject; // The object this cell is designed to represent
    
    
    @end
    

    Source:

    @interface MyTableViewCell ()
    
    @property (nonatomic, weak) IBOutlet UISlider *slider;
    
    @end
    
    @implementation MyTableViewCell
    
    - (void)setMyObject:(MyCustomObject *)myObject {
        _myObject = myObject;
    
        // Set any UI properties needed
        self.slider.value = myObject.magnitude;
    }
    
    - (void)sliderValueChanged:(UISlider *)sender {
    
        self.myObject.magnitude = sender.value;
        [self.delegate myTableViewCell:self changedSliderValue:sender.value];
    
        // Your animation code goes here as well
    
    }
    
    @end
    

    So when you dequeue the cell you would then do something like:

    MyTableViewCell *cell = ...
    cell.delegate = self;
    cell.myObject = self.myArray[indexPath.row];
    

    And if you need the notification inside the view controller when the cell slider has changed you simply implement the delegate method.