Two classes reference each other / access UITableViewCell from UITextField inside it's delegate methods? - objective-c

I have a custom UITableViewCell and a custom UITextField, with the text field inside the table view cell.
I need to do some validation on text entered in the field, and display a little tick or cross image in the cell depending on if the text entered was okay.
My table view cell class imports the text field's header, and declares a .textField property which I can use.
My text field class has a class extension (#class) of my table view cell, and declares a .cell property, which I'd like to be able to use to access the cell's image, and display / update it to the tick or cross.
The text field's delegate is my view controller, and I'm using textFieldDidEndEditing in the following way to attempt to set that image (validationConfirmation). However, it's not working, nothing's happening, no error, etc.
Any idea what I'm doing wrong? This is a puzzler.
- (void)textFieldDidEndEditing:(UITextField *)textField
{
SignUpTextField *tf = (SignUpTextField *)textField;
if (textField.text.length < 6) {
tf.cell.validationConfirmation.hidden = NO;
tf.cell.validationConfirmation.image = [UIImage imageNamed:#"cross.png"];
}
}
So here, I'm getting the text field subclass (SignUpTextField, or I'm attempting to), and trying to set its cell property's image property. Is this wrong? Messy? Bad practise?

You don't need to (and you shouldn't) have a cell reference inside textField class. I believe your textField is added as a subview of the cell. Hence in the textFieldDidEndEditing: method, you can get the cell using textField.superview. Or alternately, you can assign a tag to the textField corresponding to the tableview row that it appears in and access the cell using cellForRowAtIndexPath: method of tableview where you can use the tag to create your indexpath.

Related

TextField on Custom Cells

I am using custom cell inside tableView. In that custom cell, there is label and textField....this custom cell is being reused to display ten items. I actually want to open a datepicker on one of the cell's text field but I am not able to understand that how this work can be achieved?
All you need to do is to create an instance of UIDatePicker and set it as inputView of one of your text fields. When the text field is tapped UIKit will animate appearance of the picker for you. In turn, you'll have to handle user's input and update the text field's text.
Please set the text field tag and delegate in cellForRowAtIndexPath method...
like..
cell.yourTextFieldName.tag = 1000;
cell.yourTextFieldName.delegate = self;
In .h file add the UITextFieldDelegate
Then in the textFieldShouldBeginEditing delegate you can get the picker.
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
NSInteger txtFldTag = textField.tag;
if(txtFldTag == 1000){
//write the method to show picker.
return NO;
}

Show UITableViewCell which can then expand to show more data

I have a UITableViewCell which is currently cluttered and has too much information on the screen at once. I want to make a button, that when pushed, a small view will slide down and show the remaining information, and then when pushed again it will slide back to the original size. I was wondering how I go about doing something like this. I know NOTHING about doing this, so please be specific when pointing me in a certain direction. Thanks!
Do you want single Button for all cells in table view or you want to add button as subview on each cell
1.if you want to add button as subview on each cell
a) Add button with tag value equals to indexPath.row and set target to single method for each button.
ex
-(void)infoButonTapped:(UIButton *)sender;
Now on Click on button find the tag value of button and get the info from the array which you use to populate UITableViewCells.
b) Now create a infoview and add textview on it and set info as text prop of text view and add infoview as subview of view using UIView Animation and hide on next click
you can either use a bool variable in .h file to know hide or show infoView or you can check if infoview has superview then you have to hide infoview else add infoview as subview of self.view.
if you want a common button for all Cell
a) Now on click of button get indexPath for selected cell of tableview using tableview method
- (NSIndexPath *)indexPathForSelectedRow;
this method either return indexpath for selected cell or nil in case no cell selected
Use indexpath for get info from the array and add infoview as subview to view using step 1.b explained above.
you can also get the rect of selected row for setting the cordinate for the info view
by method of tableview
- (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath;
This may help you Show hide table
Or you can use BOOL as a property of cell and hide the subviews of UITableViewCell according to bool also resize frame of cell.
In this case, you better use the following method: -
(void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation{
}
Make a call to above method in your UITableView didselectRowAtIndexPath method.
More explanation is given in below link
UITableViewCell expand and collapse

access text in cell.textLabel.text

This seems like a simple thing but I'm stuck.
In my storyboard, I have a cell with text in it's label (using Static Cells). In didSelectRowAtIndexPath, I'm trying to access the cell's textLabel's text. Here's my code, with NSLogging:
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.textLabel.text) {
NSLog(#"cell.textLabel.text = %#",cell.textLabel.text);
} else {
NSLog(#"!cell.textLabel.text");
}
NSLog(#"just for fun: cell.textLabel.text = %#", cell.textLabel.text);
The if statement always returns "!cell.textLabel.text" and the last NSLog is always (null) although the storyboard's cell has text in it.
Is cell.textLabel not correct? Should I be looking at another subview of UITableViewCell?
The default cell style of table cells in the Storyboard are of type 'Custom'. Which gives you a blank cell where you can add other types of controls to it.
If you dragged a label onto the cell and changed its text, then you are likely using a Custom style of UITableViewCell.
The property textLabel is only available on cells of types other than Custom, such as Basic, or Detailed.
Therefore you should first check out if these styles meet your requirements and use them instead.
If you really do require a Custom type of cell, you will need to make a subclass of UITableViewCell and create outlets to access your label.

Changing NSTableCellView's objectValue in view-based NSOutlineView does not propagate to dataSource

I use a view-based NSOutlineView to display and select hierarchically structured items for a scientific application.
Each row in the outline column represents an item, signified by an item-specific icon (all the same in the picture), a checkbox that shows if the item is selected, and the name of the item. I need the icon, the checkbox and the name to appear in the same cell, hence I am using a view-based NSOutlineView.
I have implemented the NSOutlineViewDataSource protocol to supply data to the the outline view.
The method outlineView:objectValueForTableColumn:byItem: supplies a custom object that has the properties BOOL selected and NSString *name.
My custom table cell view in IB is composed as follows:
I bound the check box value to objectValue.selected and the label value to objectValue.name.
As I hoped, the outline view displays nicely the name and selection state supplied by the objectValue.
However, if I change the state of the check box, the method outlineView:setObjectValue:forTableColumn:byItem: that is defined in the NSOutlineViewDataSource protocol is not triggered in my dataSource to supply the newly changed object value. Note that if I don't use a custom view for the cell this works.
I checked whether the table cell view's objectValue.selected actually gets changed when clicking the check box by inserting an NSLog statement into the setSelected method of the object that is passed as objectValue. The selected member changes state correctly.
How do I propagate the change of the objectValue back to my dataSource's model? I have checked the NSOutlineView's delegate methods, but can't find a way to signal that the cell view's objectValue was changed by my check box (i.e., that the cell view has "ended editing"). Is there some other fundamental point I am missing?
setObjectValue doesnt work for view based ones:
from header::
/* View Based OutlineView: This method is not applicable.
*/
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
I was able to solve this problem by creating a subclass of NSTableCellView, making it the delegate of the contained NXTextField, and using the edited value to update NSTableCellView's object value.
class CategoryNameTableViewCell : NSTableCellView, NSTextFieldDelegate {
func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool {
guard var category = self.objectValue as! Category? else {
Swift.print("Tried to edit category cell with no object!")
return false
}
category.name = control.stringValue
category.saveChanges()
return true
}
}

How to select one NSCell in an NSTableView?

I have a small NSTableView with a checkbox. Whenever the checkbox is not checked, I want one of the adjacent NSCells to be grayed out and inaccessible.
However, I can't figure out how to address only one specific cell. -dataCellForRow of NSTableColumn always changes the template cell for the whole table column.
How can I access one single cell?
Edit: I fill the table view using the NSTableViewDataSource protocol.
You don't "access a cell". NSTableView asks for data only when necessary, you don't populate it or control it directly.
Instead, you create a controller object which implements the NSTableViewDatasource and optionally NSTableViewDelegate protocols. The table view then sends the datasource messages to your controller and your controller supplies the appropriate data.
You can allow editing for an object displayed in the table view by implementing the ‑tableView:setObjectValue:forTableColumn:row: datasource method. This method will be called on your controller object when the user clicks the checkbox. It is your controller's responsibility to update the model appropriately.
When the model is updated, your controller should tell the table view to reload. The table view will then ask your controller for the value of any cell that requires display using the ‑tableView:objectValueForTableColumn:row: datasource method. This will include the cell that you need to disable. Your controller needs to supply the appropriate value for the cell.
If you need more control of the cell, you can implement the
‑tableView:willDisplayCell:forTableColumn:row: delegate method. This is called just before a cell is displayed, and you can modify the cell appropriately.
More info about using data sources is in the docs.
The other option (instead of using a datasource) is to use Cocoa Bindings and an NSArrayController that you bind to your collection of model objects. In that case, you can bind the Enabled binding of the table column to some property of your model object that controls the cell's enabled state. It is your responsibility to ensure that the state of that property is correct.
If you need to make the property dependent on the value of another property, you can use the dependent key mechanism outlined in the Key-Value Observing documentation.
Could you bind the editability of that column to the value that is being displayed in the checkbox? i.e. if it is checked, it is editable, otherwise it isn't?
I am trying to remember the exact editor interface, and I am not next to my Mac at home, so I am not able to do a total walk through on it - hope this can point you in the right direction.
Since SDK Version 10.7, there's -viewAtColumn:row:makeIfNecessary: on NSTableView. The majority of information I found on the web don't take the new methods into account, so here it is for all the others looking for an answer to this question.
From Mouse Event to Cell Selection
First, add a protocol for your controller to handle cell selection from a table view, like this:
#protocol XYZCellSelectionDelegate <NSObject>
- (void)cellViewWasSelectedAtRow:(NSInteger)row column:(NSInteger)column;
#end
Then subclass NSTableView and override -mouseDown:
// In your Custom Table View subclass:
- (void)mouseDown:(NSEvent *)event
{
NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
NSInteger selectedRowIndex = [self rowAtPoint:point];
NSInteger selectedColumnIndex = [self columnAtPoint:point];
if ([self.calendarViewDelegate respondsToSelector:#selector(cellViewWasSelectedAtRow:column:)])
{
[self.calendarViewDelegate cellViewWasSelectedAtRow:selectedRowIndex column:selectedColumnIndex];
}
[super mouseDown:event];
}
Afterwards, you can use -viewAtColumn:row:makeIfNecessary: like this in the delegate/controller object:
- (void)cellViewWasSelectedAtRow:(NSInteger)row column:(NSInteger)column
{
NSView *selectedView = [self.tableView viewAtColumn:column row:row makeIfNecessary:YES];
// Do something with the cell to the right
NSInteger nextColumn = column + 1;
NSView *cellNextToIt = [self.calendarTableView viewAtColumn:nextColumn row:row makeIfNecessary:YES];
}
Note: Nowadays, I'd pass the table view to the delegate as a parameter instead of relying on the delegate to keep a reference to the table view.