3

I have a UITableView,with two sections, the second section contains a UISegmentedControl in its header. I want an option for the user to change the tableview content by selecting a segment in my UISegmentedControl. My TableView also changes the cells appearance by presenting different customcells I created with NIBs. One of my requirements is to remember the scroll position of my table view for loaded data in my tableview, so that when the user taps on my segment control to change tableview content and taps again the previous segment, my tableview will be able to present the data with the scroll position that the user remembers.

Code:

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 2;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
    if (section == kUserCoverSection) {
        return 0;
    }
    return 30;
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    if (section == kUserCoverSection) {
        return 1;
    }
    switch (self.userAchievementsType) {
        case kUserSilverCollections:
        {
            return [userSilverArray count];
        }
            break;
        case kUserBronzeCollections:
        {
            return [userBronzeArray count];
        }
            break;
        case kUserGoldsCollections:{
            return [userGoldsArray count];
        }
            break;

        default: return 0;
            break;
    }
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    if (indexPath.section == kUserCoverSection) {
        return 145;
    }else{
        switch (self.userAchievementsType) {
            case kUserSilverCollections:
            case kUserBronzeCollections:
            {
                return 40;
            }
                break;
            case kUserGoldsCollections:{
                return 358;
            }
                break;
            default:{
                return 40;
            }
                break;
        }
    }


}

-(UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section {
    if (section == kUserAchievementSection) {
        SegmentControlHeader *sectionHeaderView = [self.mTableView dequeueReusableHeaderFooterViewWithIdentifier:SegmentHeaderViewIdentifier];
        [sectionHeaderView setDelegate:self];
        [sectionHeaderView.segmentControl setSelectedSegmentIndex:self.userAchievementsType];
        return sectionHeaderView;
    }
    return nil;
}
-(UITableViewCell*)tableView:(UITableView*)aTableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
    switch (indexPath.section) {
        case kUserCoverSection:
        {
            static NSString* bannerCellType = @"kCoverBannerCellIdentifier";
            CoverBannerCell *cell = (CoverBannerCell*)[aTableView dequeueReusableCellWithIdentifier:bannerCellType];

            cell.tag = indexPath.row;
            if (cell == nil) {
                NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CoverBannerCell" owner:nil options:nil];
                cell = (CoverBannerCell*)[nib objectAtIndex:0];
            }

            return cell;
        }
        break;

        case kUserAchievementSection:
        {
            switch (self.userAchievementsType) {
                case kUserGoldsCollections:
                { 
                    static NSString* cellType = @"kUserGoldsViewCellIdentifier";
                    UserGoldsViewCell *cell = (UserGoldsViewCell*)[aTableView dequeueReusableCellWithIdentifier:cellType];
                    cell.tag = indexPath.row;
                    if (cell == nil) {
                        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"UserGoldsViewCell" owner:nil options:nil];
                        cell = (UserGoldsViewCell*)[nib objectAtIndex:0];
                    }

                    return cell;
                }
                    break;
                case kUserBronzeCollections:
                case kUserSilverCollections:{
                    static NSString* cellType = @"kUserSilverCellIdentifier";
                    UserSilverCell *cell = (UserSilverCell*)[aTableView dequeueReusableCellWithIdentifier:cellType];
                    cell.tag = indexPath.row;
                    if (cell == nil) {
                        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"UserSilverCell" owner:nil options:nil];
                        cell = (UserSilverCell*)[nib objectAtIndex:0];
                    }
                    return cell;
                }
                break;

                default: { 
                    return [self defaultCell];
                }
                break;
            }

        }
        break;

        default:
            return nil;
            break;
    }

}

-(void)didChangeSegmentValue:(UISegmentedControl *)segment{
    NSInteger index = segment.selectedSegmentIndex;
    NSInteger lastSelectedIndex = self.userAchievementsType;
    switch (lastSelectedIndex) {
        case kUserGoldsCollections: offsetsArray [kUserGoldsCollections] = @(mTableView.contentOffset.y);
            break;
        case kUserSilverCollections: offsetsArray [kUserSilverCollections] = @(mTableView.contentOffset.y);
            break;
        case kUserBronzeCollections: offsetsArray [kUserBronzeCollections] = @(mTableView.contentOffset.y);
            break;
        default:
            break;
    }
    //check if last table scroll header hit top
    if ([offsetsArray[lastSelectedIndex] floatValue] > 144) {
        for (int i = 0; i < [offsetsArray count]; i++) {
            if ([offsetsArray[i] floatValue] < 145 && offsetsArray[lastSelectedIndex] != offsetsArray[i]) {
                offsetsArray[i] = @(145);
            }
        }
    }else{
        for (int i = 0; i < [offsetsArray count]; i++) {
            if ([offsetsArray [i] floatValue] <= 145 && offsetsArray[lastSelectedIndex]!=offsetsArray[i]) {
                CGFloat newOffset;
                newOffset = [offsetsArray[i] floatValue] + ([offsetsArray[lastSelectedIndex] floatValue] -[offsetsArray[i] floatValue] );
                offsetsArray [i] = @(newOffset);
            }else{
                CGFloat newOffset = [offsetsArray[lastSelectedIndex] floatValue];
                offsetsArray [i] = @(newOffset);
            }

        }
    }
    NSRange range = NSMakeRange(1, 1);
    NSIndexSet *section = [NSIndexSet indexSetWithIndexesInRange:range];
    self.userAchievementsType = index;
    [self.mTableView beginUpdates];
    [self.mTableView reloadSections:section withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.mTableView endUpdates];
    [mTableView setContentOffset:CGPointMake(0, [offsetsArray[self.userAchievementsType] floatValue])];

}

My TableView displays properly with correct data and correct scroll positions. You would notice on my code that kUserSilverCollections and kUserBronzeCollections displays the same custom cell,and has the same height, but loads different data source.I am having this problem when the user selects kUserGoldsCollections (kUserGoldsCollections has bigger height than types kUserSilverCollections & kUserBronzeCollections) , and go back to type kUserSilverCollections or kUserBronzeCollections, the custom cells are not responding to touches anymore,buttons inside cell and the cell itself do not recognize touches, there is also a case that last few cells are responding. but the rest are not.

Im writing the code in iOS 7.1 SDK. Please help me find out what causes this problem :) Thank you!

EDIT

I found out what causes the problem, it was my UserGoldsViewCell now i feel stupid :D My question is lack of details like my cell setup. I should have included my code for my cells in this question.I Will post the solution/details as answer for future readers.

janusfidel
  • 8,036
  • 4
  • 30
  • 53
  • 2
    If you need help with your existing code then you need to post the actual code. No one can tell you what is wrong with your code by looking at made up pseudo code. – rmaddy May 10 '14 at 16:14
  • @rmaddy thank you for your suggestion. I edited the question and posted the code I currently have.. – janusfidel May 10 '14 at 16:39
  • Are you trying to position table view to the same pixel content offset or to the same logical (in term of number of cells) offset? – sha May 10 '14 at 17:01
  • 1
    I'm guessing it's messing up because you're adjusting `contentOffset` before the animation completes. This approach seems unnecessarily complex - why not just have 3 different tableviews, and use the segmented control to switch which one's visible? (They can still have the same `delegate` object.) – Aaron Brager May 12 '14 at 22:28
  • @AaronBrager same problem even when I dont implement my scrolling (setContentOffset) function. I need my data in a single `UITableView` :) – janusfidel May 12 '14 at 22:59
  • Just to be clear, your only problem is that the cells are not responding to touches after switching back to either kUserSilverCollections & kUserBronzeCollections from kUserGoldsCollections? – mj_jimenez May 13 '14 at 10:55
  • @mj_jimenez thats right. – janusfidel May 13 '14 at 11:34
  • At a first look, I don't find any problem except for the old way of load cell nibs. My only suggestion is to try to keep things simple, so instead of all the code from the range to the setContentOffset, just put a simple realoadData (in the -didChangeSegentedValue), if everything works the problem is there. Second I would not use the content offset of the table view because you have cell with different heights, better save the index path and scroll there after the update. – Andrea May 13 '14 at 11:49
  • I'm going to agree with @Andrea here. There doesn't seem to be anything wrong aside from the things she mentioned. Could the problem be in how the target action for cell subclass are setup? – mj_jimenez May 13 '14 at 12:38
  • @mj_jimenez I dont think there is wrong with my cell setup :) I implement `touchesBegan:withEvent` to detect touches. my cells (`kUserBronzeCollections / kUserSilverCollections`) can receive touches until I present the other cell and switch back to them. I also tried not to set the contentOffset. just calling `UITableView reloadData` to change the cells in `cellForFor....` . but same problem. – janusfidel May 13 '14 at 12:47
  • The fact that you are overriding touch handling is probably the source of the problem, since is touches related.. you should mention that in the question! – Andrea May 13 '14 at 13:46
  • @Andrea I override `touchesBegan:withEvent` for debugging purpose.. overriding it or not, it produces same output. – janusfidel May 13 '14 at 13:48
  • @mj_jimenez I found out where the problem is. and you are right :) it was my cell setups. – janusfidel May 14 '14 at 03:28
  • Another stylish way to accomplish this would be to maintain two different UITableViews in your view controller, and simply toggle the view of these with a segmented control. – augustzf May 19 '14 at 16:30

2 Answers2

1

I don't know why you are using this methodology for Nib:

NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"UserSilverCell" owner:nil options:nil];
cell = (UserSilverCell*)[nib objectAtIndex:0];

Use this, could be your problem:

In your viewDidLoad simply register your nib for a cellIdenfier:

[_yourTableView registerNib:[UINib nibWithNibName:@"UserSilverCell" 
                     bundle:[NSBundle mainBundle]] 
     forCellReuseIdentifier:@"kUserSilverCellIdentifier"];

and then in your cellForRowAtIndexPath:

UserSilverCell *cell = (UserSilverCell *)[tableView     
            dequeueReusableCellWithIdentifier:@"kUserSilverCellIdentifier" 
                                 forIndexPath:indexPath];

You have to apply this also to the other your type of cell.

This could be the problem, but if it isn't in any case maintain this way because is the better with Nib.

Matteo Gobbi
  • 17,697
  • 3
  • 27
  • 41
  • 3
    Pretty sure this isn't the problem, but it's a good suggestion to use a modern approach to build the cell. The OP is using a pre-os6 approach, but it's not what's wrong. – danh May 10 '14 at 18:34
  • this was my original approach in creating the cells. but produces the same problem :) thanks for the suggestion. – janusfidel May 12 '14 at 02:11
  • 1
    you have some other problem..is not this code the problem.. P.S. there are people that doesn't understand the sense of vote down...until if an answer can improve the project or the code, shouldn't down voted. – Matteo Gobbi May 13 '14 at 10:44
1

I have found what causes this problem. It was my UserGoldsViewCell that overrides hitTest:withEvent method. Below was my old implementation of the method:

- (UIView *)hitTest:(CGPoint) point withEvent:(UIEvent *)event {
    if ([self pointInside:point withEvent:event]) {
        return scrollView;
    }

    return nil;
}

I override this method because my UserGoldsViewCell has a UISrollView in it and for a reason, I need to override it. The reason why my UserSilverCell does not respond to touch event after selecting UserGoldsViewCellis because even the presented cells are UserSilverCell instance, the hitTest:withEvent in my UserGoldsViewCell still gets called and returning its' scrollView. I change my implementation for this method like below:

-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    UIView* childObj = nil;
    if ((childObj = [super hitTest:point withEvent:event]) == self)
        return self.scrollView;     
    return childObj;
}
Community
  • 1
  • 1
janusfidel
  • 8,036
  • 4
  • 30
  • 53