Jamie Jamie - 4 months ago 7
Swift Question

Getting data out of custom UITableViewCell and back into UITableViewController

I have a

UITableView
comprised of custom
UITableViewCells
. In each cell, there is a
UILabel
and a
UISlider
. Does anyone know how to, upon a change in value of one of the sliders, send the new value of the slider from the custom
UITableViewCell
(in a separate file) to the
UITableViewController
, so that I can then update the array from which the table was populated?

The closest I've got so far is a failed hack: firing a
setSelected
event when a slider value is changed. Whilst this highlights the changed custom cell, the event is not picked up by
didSelectRowAtIndexPath
in the
UITableViewController
.

Custom UITableViewCells
Whilst code is always appreciated, a conceptual/method solution is what I am looking for.

Thank you in advance,
Jamie

Answer

What you need is called Delegate Pattern.

Quoting from there to explain what does it mean:

Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object—the delegate—and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application, and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object.


These diagrams will help you understand what goes on:

Architecture:

enter image description here

Operation:

enter image description here

Now as to how to implement it, this is what you have to do.


For Objective-C:

First of all, create delegate methods of your UITableViewCell. Lets name it ContactTableViewCell.

In your ContactTableViewCell.h file, do this:

@protocol ContactCellDelegate <NSObject>
@required


-(void) didMoveSliderWithValue:(float) value;

@end



@interface ContactTableViewCell : UITableViewCell


@property (weak, nonatomic) id<ContactCellDelegate> delegate;

Now conform your TableViewController to this delegate. Let's name your VC MyTableViewController.

In MyTableViewController.h, Do this:

@interface MyTableViewController : UIViewController <ContactCellDelegate> //Use UITableViewController if you are using that instead of UIViewController.

In your cellForRowAtIndexPath, before returning cell, add this line:

cell.delegate = self;

Add implementation for the delegate method INSIDE your MyTableViewController.m.

-(void) didMoveSliderWithValue: (float) value
{
    NSLog(@"Value is : %f",value);
    //Do whatever you need to do with the value after receiving it in your VC
}

Now let's get back to your ContactTableViewCell.m. In that file you must have added some IBAction to capture the value change event in slider. Let's say it is the following:

- (IBAction)sliderValueChanged:(UISlider *)sender {

    self.myTextLabel.text = [@((int)sender.value) stringValue]; //Do whatever you need to do in cell.

 //Now call delegate method which will send value to your view controller:

   [delegate didMoveSliderWithValue:sender.value];

}

When you call delegate method, it will run the implementation that we wrote earlier in the MyTableViewController. Do whatever you need in that method.

What happens here is that your Cell sends the message to your desired VC (Which is delegate of the Cell), that "Hey, Call the delegate method that we wrote earlier in your body. I am sending you parameters right away". Your VC takes the parameters and does whatever you wanted it to do with that info and at that time.


For Swift:

First of all, your TableViewCell.swift file, create a protocol like this:

@class_protocol protocol ContactCellDelegate {
    func didMoveSliderWithValue(value: Float)
}

Now in your Cell class, create a delegate property like:

var cellDelegate: ContactCellDelegate?

In your Slider IBAction, call the delegate method like this:

self.cellDelegate?.didMoveSliderWithValue(slider.value)

In your VC do these changes:

Make it conform to the delegate:

class MyTableViewController: UIViewController, ContactCellDelegate

Add this line before returning cell in cellForRowAtIndexPath

cell.cellDelegate = self //Dont forget to make it conform to the delegate method

Add the implementation of required delegate method:

func didMoveSliderWithValue(value:float) {
            //do what you want
        }

I have kept the Swift part precise and summarized because It should be very easy to change the detailed Obj-C explanation to Swift implementation. However If you are confused about any of the pointers above, leave a comment.

Also see: Stackoverflow Documentation on using Delegate pattern to pass data back