SITUATION:
My two textfields are being declared in my .h file:
#interface DetailViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UIAlertViewDelegate, UITextFieldDelegate> {
UITextField *countTotalFieldUniversal;
UITextField *flavorNameUniversal;
}
And instantiated in my .m file:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
// Label default cell
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell==nil){
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
//Text fields used for editing
countTotalFieldUniversal = [[UITextField alloc] initWithFrame:CGRectMake(200, 10, 85, 30)];
countTotalFieldUniversal. delegate = self;
flavorNameUniversal = [[UITextField alloc] initWithFrame:CGRectMake(10, 8, 280, 30)];
flavorNameUniversal.delegate = self;
flavorNameUniversal.text = #"Flavor";
countTotalFieldUniversal.text = #"Count";
ATTEMPTS TO DEBUG:
When I try to print the current textfield value in setEditing using this code:
NSString *flavorText = ((UITextField*)[self.view viewWithTag:1]).text;
NSString *countText = ((UITextField*)[self.view viewWithTag:2]).text;
NSLog(#"newFlavorName: %#", flavorText);
NSLog(#"newCountTotal: %#", countText);
I only get the unedited, original labels: "Flavor" and "Count".
When I try to print the current textfield values in setEditing using this code:
NSString * newFlavorName = flavorNameUniversal.text;
NSString * newCountTotal = countTotalFieldUniversal.text;
NSLog(#"newFlavorName: %#", newFlavorName);
NSLog(#"newCountTotal: %#", newCountTotal);
I get null values for both!
It's like my changes to the text field aren't tracked at all!
Most likely your changes for the text field AREN'T being tracked at all! At least they are being tracked and then released before you get your hands on them.
Don't instantiate a new UITextField object every time. I suggest you subclass UITableViewCell to handle ownership and drawing of the two UITextField's. Give your subclass a two text field properties (#property (strong, nonatomic) UITextField *textField1/2) and make sure to instantiate them either in init or use lazy instantiation in the setter.
Use delegation (the textFieldDidEndEditing: method declared in the UITextFieldDelegate protocol) to get the data from your UITextField to your viewController.
That SHOULD work and track the changes made to the text field.
Related
I created a custom UITableViewCell named GateCell, inside it I placed one label and one text field.
In GateCell.h
#property (weak, nonatomic) IBOutlet UILabel *gateLabel;
#property (weak, nonatomic) IBOutlet UITextField *gateTextField;
In GateTableViewController
- (void)viewDidLoad {
[self.tableView registerClass:[GateCell class] forCellReuseIdentifier:#"cellIdentifier"];
}
Finally at cellForRowAtIndexPath method, I used like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
GateCell *cell = (GateCell *)[tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
cell.gateLabel.text = #"Gate";
cell.gateTextField.text = #"Open Gate"
return cell;
}
When I print the description of the cell, I getting the following..
<`GateCell`: 0x7b6bd790; baseClass = `UITableViewCell`; frame = (0 0; 320 44); layer = <CALayer: 0x7b6c2e60>> <br>
Printing description of cell->_gateLabel:
nil
Printing description of cell->_gateTextField:
nil
Why label and textField returns nil when cell is created ???
I have previously encountered troubles when doing registerClass:forCellReuseIdentifier: and doing a dequeueReusableCellWithIdentifier: in tableView:cellForRowAtIndexPath:.
I had to replace dequeueReusableCellWithIdentifier: and directly init cells since registerClass:forCellReuseIdentifier: had been made.
Try in tableView:cellForRowAtIndexPath: this way
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Create your custom cell GateCell the way it has to be done providing the 'reuseIdentifier'
//With a standard UITableViewCell it should be :
//UITableViewCell* cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cellIdentifier"];
GateCell *cell = [[GateCell alloc] init];
cell.gateLabel.text = #"Gate";
cell.gateTextField.text = #"Open Gate"
return cell;
}
First thing ensure that you connected the IBOutlet from the interface builder. if not then connect the IBOutlet.
for getting a cell use the following
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellReuseIdentifier"];
By calling
[self.tableView registerClass:[GateCell class] forCellReuseIdentifier:#"cellIdentifier"];
the table view will create a cell directly, not from a NIB file, so your IBOutlets won't be set (there is nowhere they could be set from).
Either you should be registering a NIB instead of a class or you should be creating the subviews as part of initWithStyle:reuseIdentifier:.
Your additional comments say that you're using a storyboard. In this case you should be setting the class of the cell you add in storyboard to GateCell and setting the cell identifier to cellIdentifier. Then, in code, you should remove the call to [self.tableView registerClass:[GateCell class] forCellReuseIdentifier:#"cellIdentifier"]; because making that call is replacing the storyboard registration...
The structure of my code is this:
UITableViewController (with one or more)-> Custom UITableviewCell (add the view of)-> UIViewController
Now, to notify an action on the UIViewController to the UITableViewController I have a protocol that follow the inverse flow explained before, but, when I do some action on UIViewController, app crashes because I'm trying to access to a deallocated instance...
I avoid the crash on IBAction on UIViewController in a dirty way: setting a property in the UIViewController as self
How can I solved this leak? This is my code:
UITableViewController:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
GameTableViewCell *cell;
cell = [tableView dequeueReusableCellWithIdentifier: CellId];
[cell configureWithGame: currentGame];
cell.delegate = self;
return cell;
}
Custom TableViewCell:
-(void)configureWithGame:(Game *)game
{
outcomeController = [[OutcomeViewController alloc] initWithGame:game];
outcomeController.delegate = self;
activeGame = game;
//Adapting outcomeView
CGRect frame = outcomeController.view.frame;
frame.size = self.outcomeView.frame.size;
outcomeController.view.frame = frame;
[[self.outcomeView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
[self.outcomeView addSubview:outcomeController.view];
}
The OutcomeViewController has a property #property (nonatomic, strong) id forceRetain; and it sets in -(void)viewDidLoad in this way:
self.forceRetain = self;
This causes some leaks and I want to solve this issue.
try setting outcomeController property to strongin your cell code.
Moreover, with the code you posted, the OutcomeViewController will be allocated every time you scroll the UITableView. Is this the behavior you want?
UITableView data disappears on touch, as per screen shots below
The data loads correctly when the view is first loaded, like so.
On touching the screen and then releasing, the data disappears. (If I touch and hold, the data is still present.)
I set a breakpoint in the UITableView custom class and noticed that the methods within (such as cellForRowAtIndexPath:) are called when the view is loaded, but not after touch. The didSelectRowAtIndexPath: method is never called.
The code is very similar to the DateCell example. I'm trying to load a DatePicker (configured to show time only) when a cell is touched.
The relevant code is below, along with a screenshot of the IB delegate and datasource connections. Please let me know if you need any more info. I am new to iOS, so I would greatly appreciate as much detail of possible causes and solutions as possible.
#interface ScheduleTableViewController ()
#property (nonatomic, strong) NSArray *dataArray;
#property (nonatomic, strong) NSDateFormatter *timeFormatter;
#property (nonatomic, strong) NSIndexPath *timePickerIndexPath;
#property (assign) NSInteger pickerCellRowHeight;
#property (nonatomic, strong) IBOutlet UIDatePicker *pickerView;
#property (nonatomic, strong) IBOutlet UIBarButtonItem *doneButton; //to be used later for ios 6 compatability
#end
#implementation ScheduleTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableDictionary *itemOne = [[#{ kPeriodKey : #" Tap a cell to change the survey time: " } mutableCopy ] autorelease];
NSMutableDictionary *itemTwo = [[#{ kPeriodKey : #"Morning Survey",
kTimeKey : [NSDate date] } mutableCopy] autorelease];
NSMutableDictionary *itemThree = [[#{ kPeriodKey : #"Evening Survey",
kTimeKey : [NSDate date] } mutableCopy] autorelease];
self.dataArray = #[itemOne, itemTwo, itemThree];
self.timeFormatter = [[[NSDateFormatter alloc] init] autorelease];
[self.timeFormatter setDateFormat:#"h:mm a"];
[self.timeFormatter setDateStyle:NSDateFormatterNoStyle];
[self.timeFormatter setTimeStyle:NSDateFormatterShortStyle];
UITableViewCell *pickerViewCellToCheck = [self.tableView dequeueReusableCellWithIdentifier:kTimePickerID];
self.pickerCellRowHeight = pickerViewCellToCheck.frame.size.height;
[self.tableView setDelegate:self];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.reuseIdentifier == kDayPeriodAndTimeCellID) {
// todo check for ios < 7.0
[self displayInlineTimePickerForRowAtIndexPath:indexPath];
} else {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
NSString *cellID = kDayPeriodAndTimeCellID;
if ([self indexPathHasPicker:indexPath]) {
cellID = kTimePickerID;
}
cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
if (indexPath.row == 0) {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
NSInteger modelRow = indexPath.row;
if (self.timePickerIndexPath != nil && self.timePickerIndexPath.row < indexPath.row) {
modelRow--;
}
NSDictionary *itemData = self.dataArray[modelRow];
if ([cellID isEqualToString:kDayPeriodAndTimeCellID]) {
cell.textLabel.text = [itemData valueForKey:kPeriodKey];
cell.detailTextLabel.text = [self.timeFormatter stringFromDate:[itemData valueForKey:kPeriodKey]];
}
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([self hasInlineTimePicker]) {
NSInteger numRows = self.dataArray.count;
return ++numRows;
}
return self.dataArray.count;
}
I am adding to an existing application, so all of the existing UI is implemented in XIB files, however this table is implemented in a storyboard. Here is a screen shot of the delegate and datasource outlet connections.
I think you need to provide a little more context here, but I have a guess. A UITableView needs a UITableViewDataSource to tell it what data should be in the cells. Do you hook that up in the XIB? If so, I don't see any of the methods implemented in your ScheduleTableViewController.
Make sure you implement that protocol and specifically the method "tableView:cellForRowAtIndexPath:". This is where you actually configure the cells that appear in the table view.
It sounds like you created the prototype table cells in InterfaceBuilder. For each prototype cell you created, make sure you set it's cell identifier in the properties inspector. Then use this cell identifier to identify which cell you are configuring in the call to "tableView:cellForRowAtIndexPath:".
Have you tried setting your table data with the functions
numberOfSectionsInTableView:tableView
tableView:tableView numberOfRowsInSection:section
I had the similar issue (in swift). It was solved by putting an instance variable to my tableViewManager responsible for presenting tableView. Initially an instance of it was created and called from button action method. So it was deallocated by ARC and consequently tableView had disappeared when I was trying to interact with it.
Inspired by: iOS TableViewController in PopOver disappears after touching tableview
In my case I was using SwipeMenuViewController (https://github.com/yysskk/SwipeMenuViewController)
and in each page there was a table view.
When I click the tabs it's switch the tables with no problem. but when I touched the table the data disappeared although
the table exists (I used background colours to check)
My solution was to hold a reference to the UIViewControllers return from the SwipeMenuViewController method:
func swipeMenuView(_ swipeMenuView: SwipeMenuView, viewControllerForPageAt index: Int) -> UIViewController {
viewControllers[index.description] = baseContactsTableViewController
return baseContactsTableViewController
}
I have a UITableView tall enough that it necessitates scrolling. The top-most cell in the table contains a UITextField for the user to enter some text.
The standard way to build this might be to create and add the text field and add it to a cell created or recycled in cellFOrRowAtIndexPath: However, this constant re-creation means that the text entered in the field is erased when the cell is scrolled out and back into view.
The solutions I've found so far suggest using UITextField delegation to track the text as it changes and store it in an iVar or property. I would like to know why this is recommended instead of the simpler approach I am using:
I am creating the UITextField in the init method of the UITableViewController and immediately storing it in a property. In cellFOrROwAtIndexPath I am simply adding the pre-existing field instead of initializing a new one. The cell itself can be recycled without issue, but because I am always using the one and only UITextField, the content is maintained.
Is this a reasonable approach? What might go wrong? Any improvements (perhaps I could still create the field in cellForRowAtIndexPath but first check if the property is nil?)
When you are creating cells in cellForRowAtIndexPath you have to use one reusable identifier for that first cell (ie. cellId1) and another for the rest (ie. cellId2).
If you do this, when you get the cell for the first element by calling [tableView dequeueReusableCellWithIdentifier:#"cellId1"] you will always get the same Object and will not be reused by other cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = nil;
// Only for first row
if (indexPath.row == 0) {
static NSString *cellId1 = #"cellId1";
cell = [tableView dequeueReusableCellWithIdentifier:cellId1];
if (cell == nil) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId1];
}
}
else {
static NSString *cellId2 = #"cellId2";
cell = [tableView cellId2];
if (cell == nil) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault cellId2];
}
}
// do whatever
return cell;
}
If there is only one UITextField, then I agree that your approach would be better/same as compared to using UITextField delegation (I think).
However, let us assume that you want to "expand" your view so that there are about 7-8 or more TextFields now. Then if you go about using your approach, then the problem will be that you will be storing 7-8 or more TextFields in memory and maintaining them.
In such a situation, a better approach would be that you create only that number of textfields as visible on screen. Then you create a dictionary which would maintain the content present in the textfield (which you can get by UITextFieldDelegate methods). This way, the same textfield can be used when the cell is reused. Only the values will change and will be dictated by the values in the dictionary.
On a sidenote, do minimal creation in cellForRowAtIndexPath as that is called during every table scroll and so creating a textField in cellForRowAtIndexPath can be expensive.
#import "ViewController.h"
#import "TxtFieldCell.h"
#define NUMBER_OF_ROWS 26
#interface ViewController ()<UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tablView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tablView.datasource = self; //set textfield delegate in storyboard
textFieldValuesArray = [[NSMutableArray alloc] init];
for(int i=0; i<NUMBER_OF_ROWS; i++){
[textFieldValuesArray addObject:#""];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - TableView Datasource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TxtFieldCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TxtFieldCellId" forIndexPath:indexPath];
cell.txtField.tag = indexPath.row;
if (textFieldValuesArray.count > 0) {
NSString *strText = [textFieldValuesArray objectAtIndex:indexPath.row];
cell.txtField.text = strText;
}
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return NUMBER_OF_ROWS;
}
#pragma mark - TextField Delegate
- (void)textFieldDidEndEditing:(UITextField *)textField {
[textFieldValuesArray replaceObjectAtIndex:textField.tag withObject:textField.text];
}
I have a question about populating UITableViewCell with the selected value in calendar. So i have this tableview which is of course created when the view appears, and as i said i also i have this calendar view controller. The main idea is that users can select a date on the calendar, and after that that value should appear in the text field in tableview.
I've have a function that is returning the selected value from the calendars view controller to that other view controller, but after that i can't populate the UITextField with that value.
Any idea or previous similar experience?
Here is the code that is used in the calendar view controller
-(void)giveString:(NSString*)string{
GradesViewController *grades = [[GradesViewController alloc]init];
[grades getString:string];
NSLog(#"String : %#",string);}
and here is the class in that other view in which i want this string value from calendar in my textfield located in tableviewcell
-(void)getString:(NSString*)string{
stringDate = string;
NSLog(#"String: %#",stringDate);}
the both values appear in log...
You are never retaining "string" in [GradesViewController getString:string]
-(void)getString:(NSString*)string {
stringDate = string;
[stringDate retain];
NSLog(#"String: %#",stringDate);
}
Make sure it's released as well in order to ensure that garbage collection will clean it up.
you need to set the stringDate value in cellForRowAtIndexpath, if you are declared that textfield in your tableviewcell cellForRowAtIndexpath. If you are using custom tableviewcell, you need to pass that value to you custome tableView cell class, like how you are calling from CalendarViewController to other ViewController
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellId = #"cellId";
UITableViewCell *tableCell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (tableCell == nil) {
tableCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
UITextField *urlTxtFld = [[UITextField alloc] initWithFrame:CGRectMake(70, 10, 230, 120)];
urlTxtFld.backgroundColor = [UIColor clearColor];
urlTxtFld.text = stringDate // you need to set your text like this
urlTxtFld.autocorrectionType = UITextAutocorrectionTypeNo;
[qrCell addSubview:urlTxtView];
[urlTxtFld release];
}
}