iphone - 自定义 UITableViewCells

标签 iphone objective-c cocoa-touch uitableview

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

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

不过,将不同的自定义单元格封装在各自位置的方法效果不佳:

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

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

我不知道除了我自己是否有人理解我刚刚写的东西,但如果是的话,我将非常感谢任何关于如何以不同方式构建我的代码的建议。

非常感谢,
斯坦

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

可编辑标签.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

最佳答案

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

您应该使用表示您的单元格的自定义类来备份您的自定义 tableviewcell nib 文件,并将逻辑封装在其中,例如您的性别选择器。如果您需要将性别告知某些外部 Controller ,您可以使用委托(delegate)模式。

TableViewCell 中的自定义性别选择器

好的,让我们从 nib 文件开始,如下所示:

enter image description here

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

enter image description here

...改为设置自定义类的 TableView 单元格的类:

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

使用新单元格的表格 View

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

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

#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]);
}

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

关于iphone - 自定义 UITableViewCells,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6017299/

相关文章:

iphone - 如果更新了包含应用内购买产品的应用,会发生什么情况?

iphone - 限制用户输入字符,并在ios中将每个输入的字母转换为大写字母

ios - 在 Obj-c 中是否可以让一个动画 block 为另一个 block 设置动画?

objective-c - 如何将字符串转换并运行为图像(base64)xcode

ios - 我的油漆泄漏内存?

iOS : NSString Evaluate String Expression

iphone - iPad Storyboard 如何为模态视图 Controller 提供所需的大小

iphone - 状态栏轮换通知

objective-c - UIWebView:如何在 HTML 文本字段中插入文本

ios - 如果不拉动刷新,则强制 UIScrollView 始终以 CGPointZero 结束