2

I have a UISegmentedControl in a UITableViewCell, that I need to listen for value changes in the TableViewController.

So far I know I have to use protocols to be able to do this, but I'm not sure where to put the code. Should it be in the UITableViewCell? In the ViewController? This is all a bit confusing for me.

I need some pinpointing on where to start, or even better, some code example?

rebellion
  • 6,628
  • 11
  • 48
  • 79

3 Answers3

4

From what we talked in comments, you want to reload UITableView every time the user changes UISegmentedControl selection.

First you need to add this to your UISegmentedControl:

self.segmentedControl.addTarget(self, action: "segmentedControlIndexChanged", forControlEvents: .ValueChanged)

But do this just after UISegmentedControl initiation, and not in cellForRow like in the second answer, because it would be called on every cell reuse (which will be a lot if you have more cells than can appear on the screen).

Then you do you logic in this method:

func segmentedControlIndexChanged() {
    // change your UITableView data or even swap your UITableViews (if you have 2)
}

Please remember, that when you change the index manually, you need to call event change manually as well:

segmentedControl.sendActionsForControlEvents(.ValueChanged)
KlimczakM
  • 12,576
  • 11
  • 64
  • 83
0

You can add method in your tableViewController.

In cellForRow Method of UITableViewController add below code.

[cell.yourSegmentControl addTarget:self action:@selector(handleSegmentControlEvent:) forControlEvents: UIControlEventValueChanged];

Then implement method in same controller,

- (void)handleSegmentControlEvent:(id)sender{
}

Hope it helps.

Nilesh Patel
  • 6,318
  • 1
  • 26
  • 40
  • Calling this from `cellForRow` is a very bad idea, especially if he uses `dequeueReusableCellWithIdentifier`... it will be called too many times. – KlimczakM Apr 25 '16 at 06:07
  • @KlimczakM I reuse cells. What would be best practice? – rebellion Apr 25 '16 at 06:15
  • @Ronny-André Bendiksen It depends what exactly do you mean by "value changes in the TableViewController". Please describe what should happen to trigger `UISegmentedControl` changes. – KlimczakM Apr 25 '16 at 06:17
  • @KlimczakM " it will be called too many times." what do you mean by that? Method will be called when user tap on the Segmentcontroller only. – Nilesh Patel Apr 25 '16 at 06:20
  • @KlimczakM I have one UISegmentedControl in a tableview header that reloads tableview data with different data for the tableview. So when you change index on the segmented control, tableview should reload with different data in the tableview. – rebellion Apr 25 '16 at 06:20
  • @Nilesh Patel `cellForRow` is called on every cell reuse, which is a lot. – KlimczakM Apr 25 '16 at 06:40
  • @KlimczakM so what is the disadvantage of adding just target to UI object? Do you have any reference link where it mentioned like its not good to add method calling code in cellForRow???? Please send links. – Nilesh Patel Apr 25 '16 at 06:44
  • @Nilesh Patel Call method X times (which can be thousands in huge tables), which should be called only 1 time (we only register for event!). Do the math. – KlimczakM Apr 25 '16 at 06:52
  • @KlimczakM method will be called on when you tap on the object. Adding target on CellForRow doesn't call the method. Please clear your basics if you are thinking like adding target call method that many times. – Nilesh Patel Apr 25 '16 at 06:53
  • @Nilesh Patel http://stackoverflow.com/questions/9243238/when-does-tableviewcellforrowatindexpath-get-called. Again, why register for event X times instead of 1 time? Od course, even will be called only on index change, but why register for this so many times? It's a code smell. – KlimczakM Apr 25 '16 at 06:57
  • @KlimczakM It depend of the functionality. Its not bad way to do like that. You are reusing the cell so there is no issue in performance. Because of this you down voted my answer? – Nilesh Patel Apr 25 '16 at 07:04
  • @Nilesh Patel It's still a code smell. Nope, I did not downvote your answer. – KlimczakM Apr 25 '16 at 07:09
  • @KlimczakM "code smell" have you read something like that on Apple Doc? If not then I don't agree with you. – Nilesh Patel Apr 25 '16 at 07:11
  • @KlimczakM Also in your answer you have mentioned second answer is not correct. Please modify it if you think its not bad approach. – Nilesh Patel Apr 25 '16 at 07:12
  • @Nilesh Patel Well, you don't have to. You can make classes 1000 lines long, run every code on main thread and keep reference cycles, but they are still bad approaches. I never said in my answer that your answer is incorrect, so I have nothing to edit... I leave the conversion right now, because it's already too long. – KlimczakM Apr 25 '16 at 07:18
  • @Ronny-AndréBendiksen The approach which I have mentioned here is also correct & not affect performance of the application. Just wanted to clear you so that you don't have any confusion regarding calling method from cellForRow or not. – Nilesh Patel Apr 25 '16 at 07:19
0

Here is the code using protocol.In this sample , you can observe the value change of the segmentControl. You can know which cell's segment being tapped and know the segment data change.

// ViewController.m

#import "ViewController.h"
#import "segmentTestCell.h"

@interface ViewController ()<UITableViewDataSource, UITableViewDelegate, segmentTestCellDelegate>

@property (nonatomic, strong) UITableView *table;

@end

@implementation ViewController
#pragma mark - LifeCycle
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self.table registerClass:[segmentTestCell class] forCellReuseIdentifier:@"cellId"];
    [self.view addSubview:self.table];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - UITableViewDataSource, UITabBarDelegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 100;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 50;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    segmentTestCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellId"];
    cell.indexPath = indexPath;
    if (!cell.delegate) {
        cell.delegate = self;
    }
    return cell;
}

#pragma mark - segmentTestCellDelegate
- (void)segmentChangeAtIndexPath:(NSIndexPath *)indexPath SelectIndex:(NSInteger)selectedIndex SelectTitle:(NSString *)selectedTitle {
    NSLog(@"%lu - %lu - %@",indexPath.section,indexPath.row,selectedTitle);

    // do something by yourself
}

#pragma mark - Getter
- (UITableView *)table {
    if (!_table) {
        _table = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
        _table.allowsSelection = NO;
        _table.dataSource = self;
        _table.delegate = self;
    }
    return _table;
}

@end

// segmentTestCell.h

#import <UIKit/UIKit.h>

@protocol segmentTestCellDelegate <NSObject>
@optional
- (void)segmentChangeAtIndexPath: (NSIndexPath *)indexPath SelectIndex: (NSInteger)selectedIndex SelectTitle: (NSString *)selectedTitle;
@end

@interface segmentTestCell : UITableViewCell

@property (nonatomic, strong) UISegmentedControl *segmentControl;

@property (nonatomic, strong) NSIndexPath *indexPath;
@property (nonatomic, weak) id<segmentTestCellDelegate> delegate;

@end

// segmentTestCell.m

#import "segmentTestCell.h"

#define titles @[@"A",@"B",@"C"]

@interface segmentTestCell()

@end

@implementation segmentTestCell

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.segmentControl.frame = CGRectMake(20, 10, 100, 30);
        [self.contentView addSubview:self.segmentControl];
    }
    return self;
}

- (void)segmentValueChange: (id)sender {
    // do something
    UISegmentedControl *control = (UISegmentedControl *)sender;
    if ([self.delegate respondsToSelector:@selector(segmentChangeAtIndexPath:SelectIndex:SelectTitle:)]) {
        [self.delegate segmentChangeAtIndexPath:_indexPath SelectIndex:control.selectedSegmentIndex SelectTitle:titles[control.selectedSegmentIndex]];
    }
}

- (UISegmentedControl *)segmentControl {
    if (!_segmentControl) {
        _segmentControl = [[UISegmentedControl alloc]initWithItems:titles];
        [_segmentControl addTarget:self action:@selector(segmentValueChange:) forControlEvents:UIControlEventValueChanged];
    }
    return _segmentControl;
}

@end
stardust
  • 109
  • 4