Наличие UITextField в UITableViewCell
Я пытаюсь сделать это уже пару дней, и, прочитав массу сообщений людей, пытающихся это сделать, я все еще не могу полностью работать UITextField
в некоторых из моих UITableViewCells
, как в этом примере:
![Screenshot]()
Либо я работаю, но текст не отображается (хотя я устанавливаю его цвет в синий цвет), клавиатура переходит в поле, когда я нажимаю на него, и я не смог правильно реализовать события клавиатуры.
Я попробовал с кучей примеров от Apple (в основном UICatalog
, где есть аналогичный элемент управления), но он все еще не работает правильно.
Может ли кто-нибудь помочь мне (и всем людям, пытающимся реализовать этот контроль) и опубликовать простую реализацию UITextField
в UITableViewCell
, которая отлично работает?
Ответы
Ответ 1
Попробуйте это. Работает как прелесть для меня (на устройствах 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 = @"[email protected]";
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]()
Ответ 2
Вот решение, которое хорошо выглядит под iOS6/7/8/9.
Обновление 2016-06-10: это все еще работает с iOS 9.3.3
Спасибо за вашу поддержку, теперь это на CocoaPods/Carthage/SPM на https://github.com/fulldecent/FDTextFieldTableViewCell
В основном мы берем запас UITableViewCellStyleValue1
и сшиваем a UITextField
, где предполагается detailTextLabel
. Это дает нам автоматическое размещение для всех сценариев: iOS6/7/8/9, iPhone/iPad, Изображение/Нет изображения, Аксессуар/Нет-аксессуар, Портрет/Пейзаж, 1x/2x/3x.
![enter image description here]()
Примечание. Это использует раскадровку с ячейкой типа UITableViewCellStyleValue1
с именем "word".
- (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;
}
Ответ 3
Вот как я это достиг:
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];
}
Это может показаться немного подробным, но оно работает!
Не забудьте установить делегат!
Ответ 4
Попробуйте это. Он также может обрабатывать прокрутку, и вы можете повторно использовать ячейки без хлопот удаления ранее просмотренных субвью.
- (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];
}
Ответ 5
Это не должно быть сложно. При создании ячейки для вашей таблицы добавьте объект UITextField в представление содержимого ячейки
UITextField *txtField = [[UITextField alloc] initWithFrame....]
...
[cell.contentView addSubview:txtField]
Установите делегат из UITextField как self (т.е. ваш контролер просмотра). Дайте тег текстовому полю, чтобы вы могли определить, какое текстовое поле было отредактировано в ваших методах делегата. Клавиатура должна появиться, когда пользователь удалит текстовое поле. Я сделал это так. Надеюсь, что это поможет.
Ответ 6
Я избегал этого, вызывая метод для запуска [cell.contentView bringSubviewToFront:textField]
каждый раз, когда появлялись мои ячейки, но затем я обнаружил этот относительно простой метод:
cell.accessoryView = textField;
Кажется, что у него нет такой же проблемы с пересылкой фона, и он сам выравнивается (несколько). Кроме того, autoLabel автоматически усекает, чтобы избежать переполнения (или под ним), что удобно.
Ответ 7
подробности
- 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("didSelect cell: \(indexPath)")
}
}
func textField(editingChangedInTextField newText: String, in cell: TextFieldInTableViewCell) {
if let indexPath = tableView?.indexPath(for: cell) {
print("updated text in cell: \(indexPath), value = \"\(newText)\"")
}
}
}
Результат
![enter image description here]()
Ответ 8
Я столкнулся с той же проблемой. Кажется, что установка свойства cell.textlabel.text
приводит UILabel к фронту содержимогоView ячейки.
Добавьте textView после установки textLabel.text
или (если это невозможно):
[cell.contentView bringSubviewToFront:textField]
Ответ 9
Я действительно боролся с этой задачей на iPad, когда текстовые поля были невидимыми в UITableView, а вся строка стала синей, когда она фокусируется.
Для меня в конце концов работала техника, описанная в разделе "Техника для контента Static Row" в Apple
Руководство по программированию таблиц. Я поместил как ярлык, так и textField в UITableViewCell в NIB для представления и вытащил эту ячейку через розетку в cellForRowAtIndexPath:
. Полученный код намного опережает UICatalog.
Ответ 10
Вот как это делается, я считаю правильным. Он работает на Ipad и Iphone, когда я тестировал его. Мы должны создавать собственные собственные калькуляторы, классифицируя uitableviewcell:
начать в интерфейсеBuilder...
создайте новый UIViewcontroller, назовите его customCell (добровольцем для xib во время вашего присутствия)
Убедитесь, что customCell является подклассом uitableviewcell
стереть все представления и создать один вид, чтобы сделать его размером отдельной ячейки. сделать этот вид пользовательской ячейки подкласса. теперь создайте два других вида (дублируйте первый).
Перейдите к своему инспектору соединений и найдите 2 IBOutlets, которые вы можете подключить к этим представлениям.
-backgroundView
-SelectedBackground
подключите их к последним двум просмотрам, которые вы просто дублируете, и не беспокойтесь о них.
самое первое представление, которое расширяет 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;
}
Ответ 11
Здесь добавляется подкаст для UITableViewCell
, который заменяет detailTextLabel на редактируемый UITextField
(или, в случае UITableViewCellStyleDefault
, заменяет textLabel). Это имеет то преимущество, что позволяет повторно использовать все известные UITableViewCellStyles, аксессуарные элементы и т.д., Теперь их можно редактировать!
@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 (или cell.textLabel. > в случае UITableViewCellStyleDefault
). например,
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
Ответ 12
Для следующих событий/возвратов в нескольких UITextfield внутри UITableViewCell в этом методе я взял 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];
}
}
Чтобы захватить текст из текстового поля -
- (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;
}
}