在 UITableViewCell 中有一个 UITextField

我已经尝试了好几天了,在阅读了大量试图这样做的人的信息之后,我仍然无法在我的一些 UITableViewCells中有一个完整的工作 UITextField,就像这个例子:

Screenshot

要么我的表单工作,但文本是不可见的(虽然我设置它的颜色为蓝色) ,键盘上的领域,当我点击它,我还没有能够正确实现键盘事件。 我尝试了一些苹果的例子(主要是 UICatalog,那里有一个类似的控件) ,但它仍然不能正常工作。

有没有人可以帮助我(以及所有试图实现这个控制的人) ,并在 UITableViewCell中发布一个简单的 UITextField实现,它工作得很好?

163549 次浏览

这应该不难。为表创建单元格时,将 UITextField 对象添加到单元格的内容视图中

UITextField *txtField = [[UITextField alloc] initWithFrame....]
...
[cell.contentView addSubview:txtField]

将 UITextField 的委托设置为 self (即视图控制器)给文本字段添加一个标记,这样就可以识别在委托方法中编辑了哪个文本字段。当用户点击文本字段时,键盘应该弹出。我把它弄成这样了。希望能有帮助。

我是这样做到的:

TextFormCell.h

#import <UIKit/UIKit.h>


#define CellTextFieldWidth 90.0
#define MarginBetweenControls 20.0


@interface TextFormCell : UITableViewCell {
UITextField *textField;
}


@property (nonatomic, retain) UITextField *textField;


@end

TextFormCell.m

#import "TextFormCell.h"


@implementation TextFormCell


@synthesize textField;


- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
// Adding the text field
textField = [[UITextField alloc] initWithFrame:CGRectZero];
textField.clearsOnBeginEditing = NO;
textField.textAlignment = UITextAlignmentRight;
textField.returnKeyType = UIReturnKeyDone;
[self.contentView addSubview:textField];
}
return self;
}


- (void)dealloc {
[textField release];
[super dealloc];
}


#pragma mark -
#pragma mark Laying out subviews


- (void)layoutSubviews {
CGRect rect = CGRectMake(self.contentView.bounds.size.width - 5.0,
12.0,
-CellTextFieldWidth,
25.0);
[textField setFrame:rect];
CGRect rect2 = CGRectMake(MarginBetweenControls,
12.0,
self.contentView.bounds.size.width - CellTextFieldWidth - MarginBetweenControls,
25.0);
UILabel *theTextLabel = (UILabel *)[self textLabel];
[theTextLabel setFrame:rect2];
}

这可能看起来有点冗长,但它工作!

别忘了设置代表!

试试这个。对我来说(在 iPhone 设备上)很有效。我曾经用这个代码登录过一次。我将表视图配置为有两个部分。你当然可以去掉节条件。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {


UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:kCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;


if ([indexPath section] == 0) {
UITextField *playerTextField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
playerTextField.adjustsFontSizeToFitWidth = YES;
playerTextField.textColor = [UIColor blackColor];
if ([indexPath row] == 0) {
playerTextField.placeholder = @"example@gmail.com";
playerTextField.keyboardType = UIKeyboardTypeEmailAddress;
playerTextField.returnKeyType = UIReturnKeyNext;
}
else {
playerTextField.placeholder = @"Required";
playerTextField.keyboardType = UIKeyboardTypeDefault;
playerTextField.returnKeyType = UIReturnKeyDone;
playerTextField.secureTextEntry = YES;
}
playerTextField.backgroundColor = [UIColor whiteColor];
playerTextField.autocorrectionType = UITextAutocorrectionTypeNo; // no auto correction support
playerTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; // no auto capitalization support
playerTextField.textAlignment = UITextAlignmentLeft;
playerTextField.tag = 0;
//playerTextField.delegate = self;


playerTextField.clearButtonMode = UITextFieldViewModeNever; // no clear 'x' button to the right
[playerTextField setEnabled: YES];


[cell.contentView addSubview:playerTextField];


[playerTextField release];
}
}
if ([indexPath section] == 0) { // Email & Password Section
if ([indexPath row] == 0) { // Email
cell.textLabel.text = @"Email";
}
else {
cell.textLabel.text = @"Password";
}
}
else { // Login button section
cell.textLabel.text = @"Log in";
}
return cell;
}

结果是这样的:

login form

试试这个。它也可以处理滚动,而且您可以重用单元格,而无需删除之前添加的子视图。

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


- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [table dequeueReusableCellWithIdentifier:@"Cell"];
if( cell == nil)
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"] autorelease];


cell.textLabel.text = [[NSArray arrayWithObjects:@"First",@"Second",@"Third",@"Forth",@"Fifth",@"Sixth",@"Seventh",@"Eighth",@"Nineth",@"Tenth",nil]
objectAtIndex:indexPath.row];


if (indexPath.row % 2) {
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 200, 21)];
textField.placeholder = @"Enter Text";
textField.text = [inputTexts objectAtIndex:indexPath.row/2];
textField.tag = indexPath.row/2;
textField.delegate = self;
cell.accessoryView = textField;
[textField release];
} else
cell.accessoryView = nil;


cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}


- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
[inputTexts replaceObjectAtIndex:textField.tag withObject:textField.text];
return YES;
}


- (void)viewDidLoad {
inputTexts = [[NSMutableArray alloc] initWithObjects:@"",@"",@"",@"",@"",nil];
[super viewDidLoad];
}

我也遇到了同样的问题。似乎设置 cell.textlabel.text属性会将 UILabel 放在单元格 contentView 的前面。 在设置 textLabel.text之后添加 textView,或者(如果不可能)调用:

[cell.contentView bringSubviewToFront:textField]

为了避免这种情况,我每次出现单元格时都调用一个方法来运行 [cell.contentView bringSubviewToFront:textField],但后来我发现了这个相对简单的技术:

cell.accessoryView = textField;

似乎没有同样的背景过度粘贴问题,而且它自己(在某种程度上)调整了自己。而且,textLabel 会自动截断以避免溢出到(或在)它下面,这很方便。

在 iPad 上,我真的很难完成这个任务,在 UITableView 中,文本字段显示为不可见,当焦点出现时,整行文本都变成了蓝色。

最后对我起作用的是苹果公司的“静态行内容技术”中描述的技术 表格视图编程指南 。我将标签和 textField 放在视图的 NIB 中的 UITableViewCell 中,然后通过 cellForRowAtIndexPath:中的一个出口将该单元格拉出。生成的代码比 UICatalog 简洁得多。

我相信正确的方法。我在 Ipad 和 Iphone 上测试过了。我们必须通过对 uitableviewcell 进行分类来创建我们自己的 customCell:

从 InterfaceBuilder 开始..。 创建一个新的 UIViewcontroller,命名为 customCell (在那里志愿参加 xib) 确保 customCell 是 uitableviewcell 的子类

现在擦除所有视图并创建一个视图,使其大小与单个单元格相同。使该视图成为 customcell 子类。现在创建另外两个视图(复制第一个)。
到你的连接检查器,找到2个 IBOutlet,你现在可以连接到这些视图。

- 背景资料 - 精选背景

将这些视图连接到您刚刚复制的最后两个视图,并且不要担心它们。 扩展 customCell 的第一个视图,将标签和 uitextfield 放在其中。 进入 customCell.h 并连接你的标签和文本字段。将此视图的高度设置为75(每个单元格的高度) 好了。

在 customCell.m 文件中,确保构造函数如下所示:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:@"CustomCell"       owner:self options:nil];
self = [nibArray objectAtIndex:0];
}
return self;
}

现在创建一个 UITableViewcontroller,在这个方法中像这样使用 customCell 类:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
// lets use our customCell which has a label and textfield already installed for us


customCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
//cell = [[[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];




NSArray *topLevelsObjects = [[NSBundle mainBundle] loadNibNamed:@"NewUserCustomCell" owner:nil options:nil];
for (id currentObject in topLevelsObjects){
if ([currentObject  isKindOfClass:[UITableViewCell class]]){
cell = (customCell *) currentObject;
break;
}
}


NSUInteger row = [indexPath row];


switch (row) {
case 0:
{


cell.titleLabel.text = @"First Name"; //label we made (uitextfield also available now)


break;
}




}
return cell;

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{


return 75.0;
}

这是一个在 iOS6/7/8/9 下看起来不错的解决方案。

更新2016-06-10: iOS 9.3.3仍然可以使用

感谢大家的支持,这里是 https://github.com/fulldecent/FDTextFieldTableViewCell的 CocoaPods/Carthage/SPM

基本上,我们采取股票的 UITableViewCellStyleValue1和订书机的 UITextField地方的 detailTextLabel应该是。这使得我们可以自动放置所有场景: iOS6/7/8/9,iPhone/iPad,Image/No-Image,Accessory/No-ory,Portrait/景观,1x/2x/3x。

enter image description here

注意: 这是使用故事板和一个名为“ word”的 UITableViewCellStyleValue1类型单元格。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell = [tableView dequeueReusableCellWithIdentifier:@"word"];
cell.detailTextLabel.hidden = YES;
[[cell viewWithTag:3] removeFromSuperview];
textField = [[UITextField alloc] init];
textField.tag = 3;
textField.translatesAutoresizingMaskIntoConstraints = NO;
[cell.contentView addSubview:textField];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:cell.textLabel attribute:NSLayoutAttributeTrailing multiplier:1 constant:8]];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeTop multiplier:1 constant:8]];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeBottom multiplier:1 constant:-8]];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:cell.detailTextLabel attribute:NSLayoutAttributeTrailing multiplier:1 constant:0]];
textField.textAlignment = NSTextAlignmentRight;
textField.delegate = self;
return cell;
}

下面是 UITableViewCell的一个下拉子类,它将 DetailTextLabel替换为可编辑的 UITextField(或者,对于 UITableViewCellStyleDefault,替换 TextLabel)。这样做的好处是,它允许您重用所有熟悉的 UITableViewCellStyles、 accoryView 等,现在细节是可编辑的!

@interface GSBEditableTableViewCell : UITableViewCell <UITextFieldDelegate>
@property UITextField *textField;
@end


@interface GSBEditableTableViewCell ()
@property UILabel *replace;
@end


@implementation GSBEditableTableViewCell


- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_replace = (style == UITableViewCellStyleDefault)? self.textLabel : self.detailTextLabel;
_replace.hidden = YES;


// Impersonate UILabel with an identical UITextField
_textField = UITextField.new;
[self.contentView addSubview:_textField];
_textField.translatesAutoresizingMaskIntoConstraints = NO;
[_textField.leftAnchor constraintEqualToAnchor:_replace.leftAnchor].active = YES;
[_textField.rightAnchor constraintEqualToAnchor:_replace.rightAnchor].active = YES;
[_textField.topAnchor constraintEqualToAnchor:_replace.topAnchor].active = YES;
[_textField.bottomAnchor constraintEqualToAnchor:_replace.bottomAnchor].active = YES;
_textField.font = _replace.font;
_textField.textColor = _replace.textColor;
_textField.textAlignment = _replace.textAlignment;


// Dont want to intercept UITextFieldDelegate, so use UITextFieldTextDidChangeNotification instead
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(textDidChange:)
name:UITextFieldTextDidChangeNotification
object:_textField];


// Also need KVO because UITextFieldTextDidChangeNotification not fired when change programmatically
[_textField addObserver:self forKeyPath:@"text" options:0 context:nil];
}
return self;
}


- (void)textDidChange:(NSNotification*)notification
{
// Update (hidden) UILabel to ensure correct layout
if (_textField.text.length) {
_replace.text = _textField.text;
} else if (_textField.placeholder.length) {
_replace.text = _textField.placeholder;
} else {
_replace.text = @" "; // otherwise UILabel removed from cell (!?)
}
[self setNeedsLayout];
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ((object == _textField) && [keyPath isEqualToString:@"text"]) [self textDidChange:nil];
}


- (void)dealloc
{
[_textField removeObserver:self forKeyPath:@"text"];
}


@end

简单的使用-只要像以前一样创建您的单元格,但是现在使用 Cell.textField而不是 Cell.detailTextLabel(或者 UITableViewCellStyleDefault的情况下使用 Cell.textLabel)。例句

GSBEditableTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (!cell) cell = [GSBEditableTableViewCell.alloc initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:@"Cell"];


cell.textLabel.text = @"Name";
cell.textField.text = _editablename;
cell.textField.delegate = self; // to pickup edits
...

受到 FD 答案的启发,并加以改进

对于这个方法中 UITableViewCell 中多个 UITextfield 上的 next/return 事件,我在情节串连板中采用了 UITextField。

@interface MyViewController () {
NSInteger currentTxtRow;
}
@end
@property (strong, nonatomic) NSIndexPath   *currentIndex;//Current Selected Row


@implementation MyViewController




- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {


UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CELL" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;


UITextField *txtDetails = (UITextField *)[cell.contentView viewWithTag:100];
txtDetails.delegate = self;


txtDetails.placeholder = self.arrReciversDetails[indexPath.row];
return cell;
}




#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {


CGPoint point = [textField convertPoint:CGPointZero toView:self.tableView];
self.currentIndex = [self.tableView indexPathForRowAtPoint:point];//Get Current UITableView row
currentTxtRow = self.currentIndex.row;
return YES;
}




- (BOOL)textFieldShouldReturn:(UITextField *)textField {
currentTxtRow += 1;
self.currentIndex = [NSIndexPath indexPathForRow:currentTxtRow inSection:0];


UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:self.currentIndex];
UITextField *currentTxtfield = (UITextField *)[cell.contentView viewWithTag:100];
if (currentTxtRow < 3) {//Currently I have 3 Cells each cell have 1 UITextfield
[currentTxtfield becomeFirstResponder];
} else {
[self.view endEditing:YES];
[currentTxtfield resignFirstResponder];
}


}

从 textfield 中获取文本-

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
switch (self.currentIndex.row) {


case 0:
NSLog(@"%@",[NSString stringWithFormat:@"%@%@",textField.text,string]);//Take current word and previous text from textfield
break;


case 1:
NSLog(@"%@",[NSString stringWithFormat:@"%@%@",textField.text,string]);//Take current word and previous text from textfield
break;


case 2:
NSLog(@"%@",[NSString stringWithFormat:@"%@%@",textField.text,string]);//Take current word and previous text from textfield
break;


default:
break;
}
}

细节

  • Xcode 10.2(10E125) ,Swift 5

完整样本代码

TextFieldInTableViewCell

import UIKit


protocol TextFieldInTableViewCellDelegate: class {
func textField(editingDidBeginIn cell:TextFieldInTableViewCell)
func textField(editingChangedInTextField newText: String, in cell: TextFieldInTableViewCell)
}


class TextFieldInTableViewCell: UITableViewCell {


private(set) weak var textField: UITextField?
private(set) weak var descriptionLabel: UILabel?


weak var delegate: TextFieldInTableViewCellDelegate?


override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupSubviews()
}


private func setupSubviews() {
let stackView = UIStackView()
stackView.distribution = .fill
stackView.alignment = .leading
stackView.spacing = 8
contentView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 6).isActive = true
stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -6).isActive = true
stackView.leftAnchor.constraint(equalTo: leftAnchor, constant: 16).isActive = true
stackView.rightAnchor.constraint(equalTo: rightAnchor, constant: -16).isActive = true


let label = UILabel()
label.text = "Label"
stackView.addArrangedSubview(label)
descriptionLabel = label


let textField = UITextField()
textField.textAlignment = .left
textField.placeholder = "enter text"
textField.setContentHuggingPriority(.fittingSizeLevel, for: .horizontal)
stackView.addArrangedSubview(textField)
textField.addTarget(self, action: #selector(textFieldValueChanged(_:)), for: .editingChanged)
textField.addTarget(self, action: #selector(editingDidBegin), for: .editingDidBegin)
self.textField = textField


stackView.layoutSubviews()
selectionStyle = .none


let gesture = UITapGestureRecognizer(target: self, action: #selector(didSelectCell))
addGestureRecognizer(gesture)
}


required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
}


extension TextFieldInTableViewCell {
@objc func didSelectCell() { textField?.becomeFirstResponder() }
@objc func editingDidBegin() { delegate?.textField(editingDidBeginIn: self) }
@objc func textFieldValueChanged(_ sender: UITextField) {
if let text = sender.text { delegate?.textField(editingChangedInTextField: text, in: self) }
}
}

ViewController

import UIKit


class ViewController: UIViewController {


private weak var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
}


extension ViewController {


func setupTableView() {


let tableView = UITableView(frame: .zero)
tableView.register(TextFieldInTableViewCell.self, forCellReuseIdentifier: "TextFieldInTableViewCell")
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = UITableView.automaticDimension
tableView.tableFooterView = UIView()
self.tableView = tableView
tableView.dataSource = self


let gesture = UITapGestureRecognizer(target: tableView, action: #selector(UITextView.endEditing(_:)))
tableView.addGestureRecognizer(gesture)
}
}


extension ViewController: UITableViewDataSource {


func numberOfSections(in tableView: UITableView) -> Int { return 1 }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 2 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TextFieldInTableViewCell") as! TextFieldInTableViewCell
cell.delegate = self
return cell
}
}


extension ViewController: TextFieldInTableViewCellDelegate {


func textField(editingDidBeginIn cell: TextFieldInTableViewCell) {
if let indexPath = tableView?.indexPath(for: cell) {
print("textfield selected in cell at \(indexPath)")
}
}


func textField(editingChangedInTextField newText: String, in cell: TextFieldInTableViewCell) {
if let indexPath = tableView?.indexPath(for: cell) {
print("updated text in textfield in cell as \(indexPath), value = \"\(newText)\"")
}
}
}

结果

enter image description here