3

I am building an app in Xcode 5, and I ran into some strange behavior of UISegmentedControl.

first some info on the app i'm building:

I'm building an app in which I want to allow users to order products at registered companies. As an extra service, I want to allow them to see the orders they did, and even to filter the orders on their status: All orders, Active orders & Delivered orders. the orders are displayed in a UITableView, and I created a UISegmentedControl inside the header view to filter the orders. when the selectedSegmentIndex of that UISegmentedControl changes, it executes an NSPredicate to filter the array and display only the desired orders.

now it's working all fine, except for 1 thing: The UISegmentedControl I have does not update it's view when I select another segment. It's default selectedSegmentIndex is 'Active', because users are probably most interested in the active orders, but when I change it to 'All', the tableview displays all orders (so the predicate is working), but the view remains on the same selectedSegmentIndex.

I did a lot of research to solve this but no answer solves my problem.. my code:

    - (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{

    if(section == 0) {
        UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f,0.0f, 320, 45)]; // x,y,width,height

        //label
        UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320, 20)];
        title.text = @"Kies een filter...";
        [headerView addSubview:title];

        //segmentedcontrol
        NSArray *itemArray = [NSArray arrayWithObjects: @"Alle", @"Actief", @"Afgehandeld", nil];
        control = [[UISegmentedControl alloc] initWithItems:itemArray];
        [control setFrame:CGRectMake(0.0f, 20.0f, 320.0, 35.0)];
        control.userInteractionEnabled = YES;
        control.tintColor = [UIColor blackColor];
        [control setEnabled:YES];
        [control addTarget:self action:@selector(changeFilter:) forControlEvents:UIControlEventAllEvents];
        [headerView addSubview:control];

        //label containing selected filter info
        UILabel *info = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 55.0f, 320, 20)];
        if ([selectedFilterInfo isEqualToString:@"Alle"]) {
            info.text = @"Alle orders:";
        }
        else if ([selectedFilterInfo isEqualToString:@"Actief"]) {
            info.text = @"Alle actieve orders:";
        }
        else if ([selectedFilterInfo isEqualToString:@"Afgehandeld"]) {
            info.text = @"Alle afgehandelde orders:";
        }
        [headerView addSubview:info];

        headerView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bar.png"]];
        return headerView;
    }
}

and the action it triggers:

- (void)changeFilter:(id)sender {
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;
if (segmentedControl.selectedSegmentIndex == 0) {
    selectedFilterInfo = @"Alle";
    filteredArray = nil;
    [segmentedControl reloadInputViews];
    [segmentedControl setSelectedSegmentIndex:0];
}

else if (segmentedControl.selectedSegmentIndex == 1) {
    NSPredicate *deliveredpredicate = [NSPredicate predicateWithFormat:@"SELF.delivered contains[c] %@", @"0"];
    NSMutableArray *notDeliveredArray = [NSMutableArray arrayWithArray:[sortedArray filteredArrayUsingPredicate:deliveredpredicate]];
    filteredArray = [NSMutableArray arrayWithArray:notDeliveredArray];
    selectedFilterInfo = @"Actief";
    [segmentedControl reloadInputViews];
    [segmentedControl setSelectedSegmentIndex:1];
}

else if (segmentedControl.selectedSegmentIndex == 2) {
    NSPredicate *deliveredpredicate = [NSPredicate predicateWithFormat:@"SELF.delivered contains[c] %@", @"1"];
    NSArray *deliveredArray = [sortedArray filteredArrayUsingPredicate:deliveredpredicate];
    filteredArray = [NSMutableArray arrayWithArray:deliveredArray];
    selectedFilterInfo = @"Afgehandeld";
    [segmentedControl reloadInputViews];
    [segmentedControl setSelectedSegmentIndex:2];
}
[self.tableView reloadData];

}

I did not include the numberOfCellsInSection etcetera because the tableview part is working perfectly. the only thing that's not working is updating the view.

Any solution would be really appreciated, Thank you all in advance!!

Bug
  • 2,576
  • 2
  • 21
  • 36
Joris416
  • 4,751
  • 5
  • 32
  • 59
  • does you changeFilter method called? if not and that is the issue then replace your code with this line : [control addTarget:self action:@selector(changeFilter:) forControlEvents:UIControlEventValueChanged]; – D-eptdeveloper Sep 03 '13 at 09:46
  • the method does get called, because it is updating the tableView. – Joris416 Sep 03 '13 at 09:47
  • replace the line i mentioned in first comment and if not necessary then remove these 2 lines from all [segmentedControl reloadInputViews]; [segmentedControl setSelectedSegmentIndex:2]; and just reload table – D-eptdeveloper Sep 03 '13 at 09:50
  • I updated my method as you said but it changed nothing. the changeFilter: is still getting called without any problems, and the tableview is updated, but the view still does not change. maybe this has to do with the fact that it is programmatically inserted in a header view? – Joris416 Sep 03 '13 at 09:58
  • have you tried with both the suggestions i gave? if then also it won't work then tell me whats happening first give it a try – D-eptdeveloper Sep 03 '13 at 10:03
  • I did everything you suggested. again, everything works fine. my tableview is updated and displays the correct orders, but the Segmentedcontrol does not update it's view. – Joris416 Sep 03 '13 at 10:24
  • I am having the same issue, UISegmentedControl in sectionHeaderView not updating its view though valueChanged action gets called – kubbing Feb 17 '14 at 10:00
  • Take a look at Matthias' answer, it solved my problem – Joris416 Feb 17 '14 at 16:31

1 Answers1

7

reloadData will reload the tableView, and when you reload the tableView all headerViews will be reloaded too. When the tableView was reloaded there is a new header. And the UISegmentedControl you see now is not the one that you have tapped.

Create an ivar that holds your selected index

@implementation .. {
    NSInteger selectedIndex;
}

when creating the view restore the saved index

- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    /* ... */
    control = [[UISegmentedControl alloc] initWithItems:itemArray];
    control.selectedSegmentIndex = selectedIndex;
    /* ... */
}

save the index when changing the segmentedControl

- (void)changeFilter:(id)sender {
    UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;
    selectedIndex = segmentedControl.selectedSegmentIndex;
    if (segmentedControl.selectedSegmentIndex == 0) {
        selectedFilterInfo = @"Alle";
        filteredArray = nil;
    }
    /* ... */

    // reloads the table and all header views!
    [tableView reloadData];
}

And btw. when you use UISegmentedControls there is no need to call setSelectedSegmentIndex: on it again, the tap sets the index already. And reloadInputViews is totally useless for a UISegmentedControl. But I guess this code was just added because it didn't work. Don't forget to remove it ;-)

Matthias Bauch
  • 89,811
  • 20
  • 225
  • 247