自定义UITableViewCells

时间:2011-05-16 12:22:49

标签: iphone objective-c cocoa-touch uitableview

我真的很惊讶在执行iphone应用程序时保持代码结构良好和可读性是多么困难......但可能是因为我做错了。

我有一个注册页面,其中包含不同类型的内联可编辑数据:生日,性别,姓名,密码,电话号码。我将页面作为自定义单元格的表格视图,其中每个单元格是UITableViewCell子类的一个实例,并从其自己的nib文件中读取。这是因为我认为我可能稍后可以在其他表视图页面中重用这些不同类型的单元格。

将不同的自定义单元格封装在自己的位置的方法虽然效果不好:

  1. 谁将成为GenderCell中性别选择器的控制者?

  2. 当我必须将SignUpController作为单元nib文件的文件所有者时,我实际上如何在不同的表视图控制器中重用单元格?

  3. 我不知道除了我以外是否有人理解我刚写的内容,但如果有的话,我会非常感谢有关如何以不同方式构建代码的任何建议。

    非常感谢,
    Stine

    为了使事情更清楚(?!),让我在这里粘贴一些代码:

    EditableLabel.h

    @interface EditableLabel : UILabel {   
        UIView *inputView, *inputAccessoryView; 
    }
    
    @property (nonatomic, retain) UIView *inputView, *inputAccessoryView; 
    
    - (void) setInputView:(UIView *)aView andToolbar:(UIToolbar *)aToolbar;
    
    @end
    

    EditableLabel.m

    @implementation EditableLabel
    
    @synthesize inputView, inputAccessoryView;
    
    - (void) dealloc {
        [inputView release];
        [inputAccessoryView release];
        [super dealloc];
    }
    
    - (void) setInputView:(UIView *)aView andToolbar:(UIToolbar *)aToolbar {
        self.inputAccessoryView = aToolbar;
        self.inputView = aView;
    }
    
    - (BOOL) canBecomeFirstResponder {
        return YES;
    }
    
    - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
        [self becomeFirstResponder];
    }
    
    @end
    

    EditableCell.h

    typedef enum {
        USERNAME, PASSWORD, MOBILE, BIRTHDAY, GENDER, DESCRIPTION, CATEGORY
    } CellTag;
    
    @interface EditableCell : UITableViewCell {
        CellTag tag;    
        UIView *editPoint;
        IBOutlet UILabel *headerLabel;
    }
    
    - (void) setTag:(CellTag)aTag andHeader:(NSString *)aHeader andEditPoint:(UIView *)aView;
    
    @property (nonatomic) CellTag tag;
    @property (nonatomic, retain) UIView *editPoint;
    @property (nonatomic, retain) UILabel *headerLabel;
    
    - (IBAction) editingDone:(id)sender;
    
    - (void) showInputView;
    - (void) hideInputView;
    
    @end
    

    EditableCell.m

    @implementation EditableCell
    
    @synthesize tag, editPoint, headerLabel;
    
    - (void) dealloc {
        [editPoint release];
        [headerLabel release];
        [super dealloc];
    }
    
    - (void) setTag:(CellTag)aTag andHeader:(NSString *)aHeader andEditPoint:(UIView *)aView {
        self.tag = aTag;
        self.headerLabel.text = aHeader;
        self.editPoint = aView;
    }
    
    - (IBAction) editingDone:(id)sender {
        [self hideInputView];
    }
    
    - (void) showInputView {   
        [self.editPoint becomeFirstResponder];
    }
    
    - (void) hideInputView {
        [self.editPoint resignFirstResponder];    
    }
    
    @end
    

    EditableLabelCell.h

    @interface EditableLabelCell : EditableCell {
        IBOutlet UILabel *placeHolderLabel;
        IBOutlet EditableLabel *editableLabel;
    }
    
    @property (nonatomic, retain) UILabel *placeHolderLabel;
    @property (nonatomic, retain) EditableLabel *editableLabel;
    
    - (void) setTag:(CellTag)aTag 
          andHeader:(NSString *)aHeader 
     andPlaceHolder:(NSString *)aPlaceHolder
       andInputView:(UIView *)aView
         andToolbar:(UIToolbar *)aToolbar;
    
    - (void) setValue:(NSString *)aValue;
    
    @end
    

    EditableLabelCell.m

    @implementation EditableLabelCell
    
    @synthesize placeHolderLabel, editableLabel;
    
    - (void) dealloc {
        [placeHolderLabel release];
        [editableLabel release];
        [super dealloc];
    }
    
    - (void) setTag:(CellTag)aTag andHeader:(NSString *)aHeader andPlaceHolder:(NSString *)aPlaceHolder andInputView:(UIView *)aView andToolbar:(UIToolbar *)aToolbar {
        [super setTag:aTag andHeader:aHeader andEditPoint:self.editableLabel];
        self.placeHolderLabel.text = aPlaceHolder;
        [self.editableLabel setInputView:aView andToolbar:aToolbar];    
    }
    
    - (void) setValue:(NSString *)aValue {
        if (aValue && aValue != @"") {                
            self.placeHolderLabel.hidden = YES;
            self.editableLabel.text = aValue;        
        } else {
            self.editableLabel.text = nil;
            self.placeHolderLabel.hidden = NO;
        }
    }
    
    @end
    

    EditableGenderCell.h

    @protocol EditableGenderCellDelegate <NSObject>
    @required
      - (NSString *) getTextForGender:(Gender)aGender;
      - (void) genderChangedTo:(Gender)aGender forTag:(CellTag)aTag;
    @end
    
    @interface EditableGenderCell : EditableLabelCell <UITableViewDataSource, UITableViewDelegate> {
        id<EditableGenderCellDelegate> delegate;    
        Gender gender;
        IBOutlet UITableView *genderTable;
        IBOutlet UIToolbar *doneBar;
    }
    
    - (void) setTag:(CellTag)aTag 
        andDelegate:(id<EditableGenderCellDelegate>)aDelegate 
          andHeader:(NSString *)aHeader 
          andGender:(Gender)aGender
     andPlaceHolder:(NSString *)aPlaceHolder;
    
    @property (nonatomic, retain) id<EditableGenderCellDelegate> delegate;
    @property (nonatomic) Gender gender;
    @property (nonatomic, retain) UITableView *genderTable;
    @property (nonatomic, retain) UIToolbar *doneBar;
    
    @end
    

    EditableGenderCell.m

    @implementation EditableGenderCell
    
    @synthesize delegate, gender, genderTable, doneBar;
    
    - (void) dealloc {
        [delegate release];
        [genderTable release];
        [doneBar release];
        [super dealloc];
    }
    
    - (void) setTag:(CellTag)aTag andDelegate:(id<EditableGenderCellDelegate>)aDelegate andHeader:(NSString *)aHeader andGender:(Gender)aGender andPlaceHolder:(NSString *)aPlaceHolder {
        [super setTag:aTag andHeader:aHeader andPlaceHolder:aPlaceHolder andInputView:self.genderTable andToolbar:self.doneBar];
        self.delegate = aDelegate;
        self.gender = aGender;
        [super setValue:[self.delegate getTextForGender:aGender]];
    }
    
    #pragma mark - Table view data source
    
    - (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
        return 1;
    }
    
    - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return 2;
    }
    
    - (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] autorelease];
        }
    
        switch (indexPath.row) {
            case MALE:
                switch (self.gender) {
                    case MALE:
                        cell.accessoryType = UITableViewCellAccessoryCheckmark;    
                        break;
                    default:
                        cell.accessoryType = UITableViewCellAccessoryNone;  
                } 
                break;
            case FEMALE:
                switch (self.gender) {
                    case FEMALE:
                        cell.accessoryType = UITableViewCellAccessoryCheckmark;    
                        break;
                    default:
                        cell.accessoryType = UITableViewCellAccessoryNone; 
                }  
                break;
        }
    
        cell.textLabel.text = [self.delegate getTextForGender:indexPath.row];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    
        return cell;
    }
    
    #pragma mark - Table view delegate
    
    - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        self.gender = indexPath.row;
        [super setValue:[self.delegate getTextForGender:self.gender]];
        [self.delegate genderChangedTo:self.gender forTag:self.tag];   
        [tableView reloadData];
    }
    
    @end
    

2 个答案:

答案 0 :(得分:4)

查看我对How to make a UITableViewCell with different subviews reusable?的回答。

您应该使用代表您的单元格的自定义类备份自定义tableviewcell nib文件,并在其中封装逻辑,例如您的性别选择器。如果您需要通知某个外部控制器性别,您可以使用委托模式。

TableViewCell中的自定义性别选择器

好的,让我们从nib文件开始,看起来像这样:

enter image description here

查看层次结构,不要设置文件的所有者...

enter image description here

...而是设置自定义类的表格视图单元格的类:

enter image description here

自定义类

正如您所看到的,该类几乎为空,只提供分段控件作为属性

<强> GenderPickerTableViewCell.h

@interface GenderPickerTableViewCell : UITableViewCell 
{
    UISegmentedControl *genderPickerSegmentedControl;
}

@property (nonatomic, retain) IBOutlet UISegmentedControl *genderPickerSegmentedControl;

@end

<强> GenderPickerTableViewCell.m

#import "GenderPickerTableViewCell.h"


@implementation GenderPickerTableViewCell

@synthesize genderPickerSegmentedControl;

#pragma mark -
#pragma mark memory management

- (void)dealloc 
{   
    [genderPickerSegmentedControl release];

    [super dealloc];
}

#pragma mark -
#pragma mark initialization

- (void)awakeFromNib
{
    // initialization goes here, for example preselect a specific gender
}

@end

使用新单元格的表格视图

我只提供必要的方法来完成这项工作。 TableViewCellFactory类只是我在上面的referenced answer中发布的nib加载器。 genderPickerTableViewCellWithTableView只是一个方便类方法,可以返回那种特殊类型的单元格,而不需要太多的样板代码

要注意的最后一件重要事情是单元格的配置,这很简单,我只是直接访问分段控件并向其添加目标,通知此视图控制器有关更改。

#pragma mark -
#pragma mark view lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    tableView.rowHeight = 100.0;
    tableView.dataSource = self;
    tableView.delegate = self;
}

#pragma mark -
#pragma mark UITableView methods

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

- (UITableViewCell *) tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)anIndexPath
{
    GenderPickerTableViewCell *cell = [TableViewCellFactory genderPickerTableViewCellWithTableView:aTableView];

    [cell.genderPickerSegmentedControl addTarget:self 
                                          action:@selector(genderPicked:) 
                                forControlEvents:UIControlEventValueChanged];

    return cell;
}

#pragma mark -
#pragma mark UISegmentedControl action

- (void)genderPicked:(id)sender
{
    UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;

    NSLog(@"selected index: %d", [segmentedControl selectedSegmentIndex]);
}

我希望这对开始有所帮助。

答案 1 :(得分:1)

这是您可以用于弹跳的代码:

 -(IBAction)textFieldDidBeginEditing:(UITextField *)textField { //Keyboard becomes visible
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3];
self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y - 50, self.view.frame.size.width, self.view.frame.size.height);
[UIView commitAnimations];

}

只需在编辑开始时相应地设置y即可。