akkyie / AKPickerView

A simple yet customizable horizontal picker view.
MIT License
893 stars 106 forks source link

reloadData pulls in new data, but spacing for old data persists #44

Closed brucegreig closed 9 years ago

brucegreig commented 9 years ago

I've got an AKPickerView which changes depending on user settings. It is in a recipe conversion tool and displays cups, grams (g) or ounces (oz) depending user settings. If user selects different units, then I change the data source array for the picker.

Changing the array works fine, and the correct list of items appears according to the unit (cup/g/oz) selected by user.

But it seems that the spacing between items is dictated by the array first loaded. If I change the array, then the new items are displayed, but the spacing is wrong and the selected item gets forced off centre (at least, I think that's why it is not centered).

Looks fine on first display:

screen shot 2015-02-13 at 17 41 44

But after changing data in that picker, it goes a bit wonky:

screen shot 2015-02-13 at 17 41 22

I can work around it by using items with very similar character lengths in each data set, but is there a way to force the picker to take into account the length of the items in the new data so that items are spaced out correctly? Any help much appreciated, thank you so much!

akkyie commented 9 years ago

Hmmm, have you called [pickerView reloadData]; after changing the array?

brucegreig commented 9 years ago

Yep, definitely.

On Friday, 13 February 2015, Akkyie Y notifications@github.com wrote:

Hmmm, have you called [pickerView reloadData]; after changing the array?

— Reply to this email directly or view it on GitHub https://github.com/Akkyie/AKPickerView/issues/44#issuecomment-74297731.

akkyie commented 9 years ago

I still couldn't reproduce this problem... Do you mind showing me some pieces of code around this problem?

brucegreig commented 9 years ago

I'll do a separate app just with this flow, and verify that I can reproduce the issue in isolation. I'll let you know what I find.

Thanks so much for your prompt help!

Bruce

On Friday, 13 February 2015, Akkyie Y notifications@github.com wrote:

I still couldn't reproduce this problem... Do you mind showing me some pieces of code around this problem?

— Reply to this email directly or view it on GitHub https://github.com/Akkyie/AKPickerView/issues/44#issuecomment-74309097.

akkyie commented 9 years ago

Got it. I'm sorry I couldn't be much help.

brucegreig commented 9 years ago

I've isolated the cause. In some cases I programatically select first item in picker. (I do this to handle case where user has just changed their settings from one unit to another, leaving their previous picker selection out of range, as there are fewer items in the oz array than in the g array, for example). Then I reload the picker. If I remove that step (of programatically selecting an item in the picker before reloading the picker) then picker displays correctly.

Now I just need to figure out how to handle case where picker array is changed to a smaller size leaving selected item out of range.

I'll figure it out, but code below demonstrates what I mean if you are interested!


- (void)viewDidLoad {
    [super viewDidLoad];
    [self displayPicker];
}

-(void)viewDidAppear:(BOOL)animated {
   // Need to use viewDidAppear, so that pickers updated when user returns from a separate Settings screen

    // Force selection to first item in picker, to handle case where user had previously selected an item which is out of range of new array. (e.g. '29 inch' selected, then units changed to 'cm'. This step causes picker layout to get messed up. Comment out next line to resolve layout, but then we are exposed to out-of-range error on reloading the picker    
    [self.testPicker selectItem:0 animated:NO];
    [self displayPicker];   
}

-(void)displayPicker {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    if ([settings stringForKey:@"Units"] == nil)
    {
        [settings setObject:@"in" forKey:@"Units"];
        NSLog(@"Unit selection null so insert default");
    }

    self.testPicker.delegate = self;
    self.testPicker.dataSource = self;
    self.testPicker.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self.view addSubview:self.testPicker];
    self.testPicker.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:20];
    self.testPicker.highlightedFont = [UIFont fontWithName:@"HelveticaNeue" size:20];
    self.testPicker.interitemSpacing = 20.0;
    self.testPicker.fisheyeFactor = 0.001;
    self.testPicker.pickerViewStyle = AKPickerViewStyle3D;
    self.testPicker.hidden = NO;

    if ([[settings stringForKey:@"Units"]  isEqual: @"in"])
    {

        self.testPickerItems = @[@"1 inch",
                              @"2 inch",
                              @"3 inch",
                              @"4 inch",
                              @"5 inch",
                              @"6 inch",
                              @"7 inch",
                              @"8 inch",
                              @"9 inch",
                              @"10 inch",
                              @"11 inch",
                              @"12 inch",
                              @"13 inch",
                              @"14 inch",
                              @"15 inch",
                              @"16 inch",
                              @"17 inch",
                              @"18 inch",
                              @"19 inch",
                              @"20 inch",
                              @"21 inch",
                              @"22 inch",
                              @"23 inch",
                              @"24 inch",
                              @"25 inch",
                              @"26 inch",
                              @"27 inch",
                              @"28 inch",
                              @"29 inch",
                              @"30 inch",
                              @"31 inch",
                              @"32 inch",
                              @"33 inch",
                              @"34 inch",
                              @"35 inch",
                              @"36 inch",
                              @"37 inch",
                              @"38 inch"
                              ];
    }

    if ([[settings stringForKey:@"Units"]  isEqual: @"cm"])
    {

 // Note that this array is smaller than the inches array

        self.testPickerItems = @[@"8cm",
                              @"9cm",
                              @"10cm",
                              @"11cm",
                              @"12cm",
                              @"13cm",
                              @"14cm",
                              @"15cm"];
    }

    [self.testPicker reloadData];
}
akkyie commented 9 years ago

I reproduced the problem and this is definitely a problem, but I found a workaround. Separate selecting the first item and setting data, like:

- (void)viewWillAppear:(BOOL)animated {
    [self.pickerView selectItem:0 animated:NO];
}

- (void)viewDidAppear:(BOOL)animated {
    if (...) {
        self.testPickerItems = ...
    }
    [self.pickerView reloadData];
}

or you don't want to use viewWillApper, you can use dispatch_after.

I think the cause is an internal conflict between two commands for UICollectionView, selecting and reloading. I can't deny there might be a better implementation for AKPickerView, but I suppose it is partly because of the specification of UICollectionView. For now, please use the workaround above.

brucegreig commented 9 years ago

Ah, OK, that looks like a perfect workaround. Thank you so much!

akkyie commented 9 years ago

I hope you makes a great app.

brucegreig commented 9 years ago

Just to let you know, my (very simple) app is now in the App Store, using your horizontal pickers:

Cake Tin Calculator

This is the first app I've built from scratch myself, and I am very pleased with it! Thank you for your help!

akkyie commented 9 years ago

Great! Good luck to your app.