【问题标题】:UIPickerView Inside UITableView Using DateCellUIPickerView 在 UITableView 内使用 DateCell
【发布时间】:2014-04-23 08:50:16
【问题描述】:

我使用了来自以下链接DateCell without storyboard 的修改后的 DateCell 代码。

我想用UIPickerView 替换UIDatePicker。下面是我修改/评论的代码,使其适用于UIPickerView

在每次点击单元格时,我都能够显示UIPickerView,但它只加载一次,并且对于具有相同行标题的其余单元格UIPickerView 会出现,并且当我从UIPickerView 中选择一行时它会崩溃。在过去的几天里,我陷入了困境。

任何帮助将不胜感激。

MoreTableViewController.h

#import <UIKit/UIKit.h>

@interface MoreTableViewController : UITableViewController<UIPickerViewDataSource, UIPickerViewDelegate>

@property(nonatomic,retain)NSArray *ASOR;
@property(nonatomic,retain)NSArray *ASQT;
@property(nonatomic,retain)NSArray *respectiveOptions;

@end

MoreTableViewController.m

#import "MoreTableViewController.h"
#import "AppDelegate.h"

#define kPickerAnimationDuration    0.40   // duration for the animation to slide the date picker into view
#define kDatePickerTag              99     // view tag identifiying the date picker view

//#define kTitleKey       @"title"   // key for obtaining the data source item's title
//#define kDateKey        @"date"    // key for obtaining the data source item's date value

// keep track of which rows have date cells
#define kDateStartRow   1
#define kDateEndRow     2

static NSString *kDateCellID = @"dateCell";     // the cells with the start or end date
static NSString *kDatePickerID = @"datePicker"; // the cell containing the date picker
static NSString *kOtherCell = @"otherCell";     // the remaining cells at the end

#pragma mark -

@interface MoreTableViewController ()

@property (nonatomic, strong) NSArray *dataArray;
//@property (nonatomic, strong) NSDateFormatter *dateFormatter;

// keep track which indexPath points to the cell with UIDatePicker
@property (nonatomic, strong) NSIndexPath *datePickerIndexPath;

@property (assign) NSInteger pickerCellRowHeight;

@property (nonatomic, strong) UIPickerView *pickerView;

@end

@implementation MoreTableViewController
@synthesize ASOR, ASQT, respectiveOptions;

- (UITableViewCell *) createCellWithIdetifier:(NSString *)cellId {
    NSArray *reusableUiComponents = [[NSBundle mainBundle] loadNibNamed:@"ReusableUIComponents" owner:self options:nil];

    if ([kDateCellID isEqualToString:cellId]) {
        return reusableUiComponents[0];
    }
    if ([kDatePickerID isEqualToString:cellId]) {
        return reusableUiComponents[1];
    }
//    if ([kOtherCell isEqualToString:cellId]) {
//        return reusableUiComponents[2];
//    }
    return nil;
}

- (AppDelegate *)appDelegate {
    return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)setupDataSource {
    // setup our data source
//    NSMutableDictionary *itemOne = [@{ kTitleKey : @"Tap a cell to change its date:" } mutableCopy];
//    NSMutableDictionary *itemTwo = [@{ kTitleKey : @"Start Date",
//                                       kDateKey : [NSDate date] } mutableCopy];
//    NSMutableDictionary *itemThree = [@{ kTitleKey : @"End Date",
//                                         kDateKey : [NSDate date] } mutableCopy];
//    NSMutableDictionary *itemFour = [@{ kTitleKey : @"(other item1)" } mutableCopy];
//    NSMutableDictionary *itemFive = [@{ kTitleKey : @"(other item2)" } mutableCopy];
    self.dataArray = [[self appDelegate]advanceSearchQuestionsText];

    //self.dateFormatter = [[NSDateFormatter alloc] init];
    //[self.dateFormatter setDateStyle:NSDateFormatterShortStyle];    // show short-style date format
    //[self.dateFormatter setTimeStyle:NSDateFormatterNoStyle];

    // obtain the picker view cell's height, works because the cell was pre-defined in our storyboard
    UITableViewCell *pickerViewCellToCheck = [self createCellWithIdetifier:kDatePickerID];
    self.pickerCellRowHeight = pickerViewCellToCheck.frame.size.height;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self setupDataSource];

    //self.title = @"DateCell";

    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

    ASQT = [[NSArray alloc]init];
    ASOR = [[NSArray alloc]init];
    ASQT = [[self appDelegate]advanceSearchQuestionsText];
    ASOR = [[self appDelegate]advanceSearchOptionsArray];
}

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

#pragma mark - Utilities

/*! Returns the major version of iOS, (i.e. for iOS 6.1.3 it returns 6)
 */
NSUInteger UNUSED_DeviceSystemMajorVersion() // TODO - move this to Utils
{
    static NSUInteger _deviceSystemMajorVersion = -1;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        _deviceSystemMajorVersion = [[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."] objectAtIndex:0] intValue];
    });

    return _deviceSystemMajorVersion;
}

/*! Determines if the given indexPath has a cell below it with a UIDatePicker.

 @param indexPath The indexPath to check if its cell has a UIDatePicker below it.
 */
- (BOOL)hasPickerForIndexPath:(NSIndexPath *)indexPath
{
    BOOL hasDatePicker = NO;

    NSInteger targetedRow = indexPath.row;
    targetedRow++;

    UITableViewCell *checkDatePickerCell =
    [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:targetedRow inSection:0]];
    UIPickerView *checkDatePicker = (UIPickerView *)[checkDatePickerCell viewWithTag:kDatePickerTag];

    hasDatePicker = (checkDatePicker != nil);
    return hasDatePicker;
}

/*! Updates the UIDatePicker's value to match with the date of the cell above it.
 */
- (void)updateDatePicker
{
    if (self.datePickerIndexPath != nil)
    {
        UITableViewCell *associatedDatePickerCell = [self.tableView cellForRowAtIndexPath:self.datePickerIndexPath];

        UIPickerView *targetedDatePicker = (UIPickerView *)[associatedDatePickerCell viewWithTag:kDatePickerTag];
        if (targetedDatePicker != nil)
        {
            // we found a UIDatePicker in this cell, so update it's date value
            //
//            NSDictionary *itemData = self.dataArray[self.datePickerIndexPath.row - 1];
//            [targetedDatePicker setDate:[itemData valueForKey:kDateKey] animated:NO];

            // set the call action for the date picker to dateAction
//            [targetedDatePicker addTarget:self action:@selector(dateAction:) forControlEvents:UIControlEventValueChanged];
        }
    }
}

/*! Determines if the UITableViewController has a UIDatePicker in any of its cells.
 */
- (BOOL)hasInlineDatePicker
{
    return (self.datePickerIndexPath != nil);
}

/*! Determines if the given indexPath points to a cell that contains the UIDatePicker.

 @param indexPath The indexPath to check if it represents a cell with the UIDatePicker.
 */
- (BOOL)indexPathHasPicker:(NSIndexPath *)indexPath
{
    return ([self hasInlineDatePicker] && self.datePickerIndexPath.row == indexPath.row);
}

/*! Determines if the given indexPath points to a cell that contains the start/end dates.

 @param indexPath The indexPath to check if it represents start/end date cell.
 */
- (BOOL)indexPathHasDate:(NSIndexPath *)indexPath
{
    BOOL hasDate = NO;

    if ((indexPath.row == kDateStartRow) ||
        (indexPath.row == kDateEndRow || ([self hasInlineDatePicker] && (indexPath.row == kDateEndRow + 1))))
    {
        hasDate = YES;
    }

    return hasDate;
}

#pragma mark - Table view data source

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat height = ([self indexPathHasPicker:indexPath] ? self.pickerCellRowHeight : self.tableView.rowHeight);
    return height;


}

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

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if ([self hasInlineDatePicker])
    {
        // we have a date picker, so allow for it in the number of rows in this section
        NSInteger numRows = self.dataArray.count;
        return ++numRows;
    }

    return self.dataArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = nil;

    NSString *cellID = kDateCellID;

    if ([self indexPathHasPicker:indexPath])
    {
        // the indexPath is the one containing the inline date picker
        cellID = kDatePickerID;     // the current/opened date picker cell
    }
    else if ([self indexPathHasDate:indexPath])
    {
        // the indexPath is one that contains the date information
        cellID = kDateCellID;       // the start/end date cells
    }

    cell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if (!cell) {
        cell = [self createCellWithIdetifier:cellID];
    }

    if (indexPath.row == 0)
    {
        // we decide here that first cell in the table is not selectable (it's just an indicator)
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    // if we have a date picker open whose cell is above the cell we want to update,
    // then we have one more cell than the model allows
    //
    NSInteger modelRow = indexPath.row;
    if (self.datePickerIndexPath != nil && self.datePickerIndexPath.row < indexPath.row)
    {
        modelRow--;
    }

    NSDictionary *itemData = self.dataArray[modelRow];

    // proceed to configure our cell
    if ([cellID isEqualToString:kDateCellID])
    {
        // we have either start or end date cells, populate their date field
        //
        cell.textLabel.text = [self.dataArray objectAtIndex:indexPath.row];
        //cell.detailTextLabel.text = [self.dateFormatter stringFromDate:[itemData valueForKey:kDateKey]];
    }
//    else if ([cellID isEqualToString:kOtherCell])
//    {
//        // this cell is a non-date cell, just assign it's text label
//        //
//        //cell.textLabel.text = [itemData valueForKey:kTitleKey];
//        cell.textLabel.text = [self.dataArray objectAtIndex:indexPath.row];
//    }

    return cell;
}

/*! Adds or removes a UIDatePicker cell below the given indexPath.

 @param indexPath The indexPath to reveal the UIDatePicker.
 */
- (void)toggleDatePickerForSelectedIndexPath:(NSIndexPath *)indexPath
{
    [self.tableView beginUpdates];

    NSArray *indexPaths = @[[NSIndexPath indexPathForRow:indexPath.row + 1 inSection:0]];

    // check if 'indexPath' has an attached date picker below it
    if ([self hasPickerForIndexPath:indexPath])
    {
        // found a picker below it, so remove it
        [self.tableView deleteRowsAtIndexPaths:indexPaths
                              withRowAnimation:UITableViewRowAnimationFade];
    }
    else
    {
        // didn't find a picker below it, so we should insert it
        [self.tableView insertRowsAtIndexPaths:indexPaths
                              withRowAnimation:UITableViewRowAnimationFade];
    }

    [self.tableView endUpdates];
}

/*! Reveals the date picker inline for the given indexPath, called by "didSelectRowAtIndexPath".

 @param indexPath The indexPath to reveal the UIDatePicker.
 */
- (void)displayInlineDatePickerForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // display the date picker inline with the table content
    [self.tableView beginUpdates];

    BOOL before = NO;   // indicates if the date picker is below "indexPath", help us determine which row to reveal
    if ([self hasInlineDatePicker])
    {
        before = self.datePickerIndexPath.row < indexPath.row;
    }

    BOOL sameCellClicked = (self.datePickerIndexPath.row - 1 == indexPath.row);

    // remove any date picker cell if it exists
    if ([self hasInlineDatePicker])
    {
        [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.datePickerIndexPath.row inSection:0]]
                              withRowAnimation:UITableViewRowAnimationFade];
        self.datePickerIndexPath = nil;
    }

    if (!sameCellClicked)
    {
        // hide the old date picker and display the new one
        NSInteger rowToReveal = (before ? indexPath.row - 1 : indexPath.row);
        NSIndexPath *indexPathToReveal = [NSIndexPath indexPathForRow:rowToReveal inSection:0];

        [self toggleDatePickerForSelectedIndexPath:indexPathToReveal];
        self.datePickerIndexPath = [NSIndexPath indexPathForRow:indexPathToReveal.row + 1 inSection:0];
    }

    // always deselect the row containing the start or end date
    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];

    [self.tableView endUpdates];

    // inform our date picker of the current date to match the current cell
    [self updateDatePicker];
}


#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    respectiveOptions = [[NSArray alloc]init];


    respectiveOptions = [ASOR objectAtIndex:indexPath.row];


    if ([kDateCellID isEqualToString:cell.reuseIdentifier])
    {

        [self displayInlineDatePickerForRowAtIndexPath:indexPath];
    }
    else
    {
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
    }
}

- (UIPickerView*)pickerViewForCell:(UITableViewCell*)cell {
    UIPickerView * picker = [[UIPickerView alloc] initWithFrame:cell.bounds];
    [picker setDelegate:self];
    [picker setDataSource:self];
    picker.hidden = YES;
    [cell addSubview:picker];
    return picker;
}

#pragma mark - Actions

/*! User chose to change the date by changing the values inside the UIDatePicker.

 @param sender The sender for this action: UIDatePicker.
 */
//- (void)dateAction:(id)sender
//{
//    NSIndexPath *targetedCellIndexPath = nil;
//    
//    if ([self hasInlineDatePicker])
//    {
//        // inline date picker: update the cell's date "above" the date picker cell
//        //
//        targetedCellIndexPath = [NSIndexPath indexPathForRow:self.datePickerIndexPath.row - 1 inSection:0];
//    }
//    else
//    {
//        // external date picker: update the current "selected" cell's date
//        targetedCellIndexPath = [self.tableView indexPathForSelectedRow];
//    }
//    
//    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:targetedCellIndexPath];
//    UIDatePicker *targetedDatePicker = sender;
//    
//    // update our data model
//    NSMutableDictionary *itemData = self.dataArray[targetedCellIndexPath.row];
//    [itemData setValue:targetedDatePicker.date forKey:kDateKey];
//    
//    // update the cell's date string
//    cell.detailTextLabel.text = [self.dateFormatter stringFromDate:targetedDatePicker.date];
//}

// Number of components.
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}

// Total rows in our component.
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return [respectiveOptions count];

}

// Display each row's data.
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{

    return [respectiveOptions objectAtIndex:row];


}

// Do something with the selected row.
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{

}

【问题讨论】:

  • 在显示UIPickerView之前使用[pickerView reloadAllComponents];
  • 我在- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 中添加了[pickerView reloadAllComponents];。一旦我在pickerView 中选择一行,它就会崩溃并更改pickerView 的标题。

标签: ios objective-c uitableview uipickerview


【解决方案1】:

这是UITableView 使用嵌入在单元格中的UIPickerView 的示例。

我不会更改选择器视图的颜色、单元格、单元格的高度 - 我会留给你做一些适合你需要的事情。

但这里的主要思想是向您展示如何在单元格中拥有选择器视图并能够相应地获得价值。

@interface YourController ()
@property (strong, nonatomic) NSMutableArray *pickerViewsArray;
@end

@implementation YourController

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self)
    {
        // Custom initialization
    }
    return self;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.pickerViewsArray = [[NSMutableArray alloc] init];
    [self createYourPickerViews];
}

- (void)createYourPickerViews
{
    for(int x = 0; x < 10; x++) //number of picker views
    {
        UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:(CGRect){{0, 0}, 150, 10}];
        pickerView.delegate = self;
        pickerView.dataSource = self;
        pickerView.tag = x;
        [self.pickerViewsArray addObject:pickerView];
    }
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

    // Return the number of rows in the section.
    return [self.pickerViewsArray count]; //As mentioned before, this is important.
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if(cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    // Configure the cell...
    cell.textLabel.text = [NSString stringWithFormat:@"%i", indexPath.row];
    [cell.contentView addSubview:(UIPickerView*)[self.pickerViewsArray objectAtIndex:indexPath.row]];

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 150.0f; //just some arbitrary value, change it to suit your needs.
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    //tag corresponds to row on tables view.
    NSLog(@"view tag:%ld", (long)pickerView.tag);
    //row here corresponds to the value selected from picker view.
    NSLog(@"view value:%ld", (long)row);
}

- (NSString*) pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    return [NSString stringWithFormat:@"%d", row+1];  
}

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 1;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    //change this value to suit your needs.
    return 10;
}

@end

最后确保在您的 .h 中执行此操作:

@interface YourController : UITableViewController <UITableViewDataSource, UITableViewDelegate, UIPickerViewDelegate, UIPickerViewDataSource>

这样,您就可以相应地从每个不同单元格的选取器视图中检索值。

对于 UI,我把它留给你。代码经过测试。希望这会有所帮助。

评论后(更新 2):

我再次运行它,我明白你的意思了,虽然昨天我没有发生这种情况。 我对以下内容进行了更改并运行了另一个测试,这将解决它:

cellForRowAtIndexPath:

if(cell == nil)
{
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.textLabel.text = [NSString stringWithFormat:@"%i", indexPath.row];
    [cell.contentView addSubview:[self.pickerViewsArray objectAtIndex:indexPath.row]];
}

另一个更新(关于 UI:点击单元格时如何隐藏和取消隐藏选择器):

修改为:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIPickerView *temp;
    temp = [self.pickerViewsArray objectAtIndex:indexPath.row];

    if(!temp.isUserInteractionEnabled)
    {
        [UIView animateWithDuration:1.0f animations:^
        {
            temp.userInteractionEnabled = YES;
            temp.layer.opacity = 1.0f;
        }
        completion:^(BOOL finished)
        {
        }];
    }
    else
    {
        [UIView animateWithDuration:1.0f animations:^
        {
            temp.userInteractionEnabled = NO;
            temp.layer.opacity = 0.0f;
        }
        completion:^(BOOL finished)
        {
        }];
    }
}

在:

- (void)createYourPickerViews
{
    for(int x = 0; x < 10; x++)
    {
        UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:(CGRect){{0, 0}, 150, 10}];
        pickerView.delegate = self;
        pickerView.dataSource = self;
        pickerView.layer.opacity = 0.0f;  ======>>> //Add this
        pickerView.userInteractionEnabled = NO; =======>>> //And this
        pickerView.tag = x;
        [self.pickerViewsArray addObject:pickerView];
    }
}

再次测试代码。希望这(最终)有所帮助。

【讨论】:

  • 谢谢,它正在工作,但我认为有一个错误。当您向下滚动表格并从选择器中选择值时,它会自动在其各自的表格单元格以及底部的单元格上加载两次选择器。
  • 我还使用苹果提供的DateCell 实现了内联选择器。其中有几个我也无法弄清楚的错误。 1. 当pickerView 打开并向下滚动表格时,它会崩溃。你也可以看看吗?您可以从here 找到它。我认为这是因为 indexPath 的值,但我无法修复它。
  • 知道如何在不需要pickerView 时隐藏它,就像我们在DateCell 中看到的那样?当我们点击出现的表格行时,我们执行功能,再次点击表格行,pickerView 消失。
  • 非常感谢,但我指的是DateCell,这样当用户点击桌子上的一行时,它会改变它的高度,展开并显示pickerView,当用户点击同一行时它再次回到原来的高度。
猜你喜欢
  • 2017-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多