How do I use a UISegmentedControl to control UITableView cells? - objective-c

I've successfully set up a Tab Bar controller with. Table View in the first view and a simple UIView in the second tab. The UIView has it's own class. I've added a segmented control to the latter and ultimately I want to change the images used in the table view cells depending on which segment is selected.
The cell images are part of an array and have been set up using a custom cell.
I've set up the segmented control in the UIView class adding #property an #syntesize but I can't for the life of me figure out how to change the cell images from the UIView class.
Thanks in advance!
The segment control in my UIView.h is defined like so
#property (weak, nonatomic) IBOutlet UISegmentedControl *selectedSegment
I've not yet included an IBAction as I'm not 100% where it would need to go or how it would be written.
In my table view my cell images are currently defined like so in cellForRowAtIndexPath
cell.itemImageView.image = [UIImage imageNamed:[icons objectAtIndex:indexPath.row]];
My thoughts were to put a switch statement within the cell method with each case pointing to a different icon set so
switch (something)
{
case 0:
cell.itemImageView.image = [UIImage imageNamed:[iconSetOne objectAtIndex:indexPath.row]];
break;
case 1:
cell.itemImageView.image = [UIImage imageNamed:[iconSetTwo objectAtIndex:indexPath.row]];
break;
default
break;
}
But I'm pretty sure that's the wrong way of doing it and the "something" bit is what I'm stuck on.

1) In the simple UIView where you have your segmented control:
Define an outlet _tableViewController that you connect to the controller of your table view
Define an action method segmentedControlChanged: that responds to the segmented control being triggered
2) In the table view controller:
Define a property selectedSegmentIndex of type NSInteger
3) Now add the implementation for the action method:
- (void) segmentedControlChanged:(id)sender
{
UISegmentedControl* segmentedControl = (UISegmentedControl*)sender;
_tableViewController.selectedSegmentIndex = segmentedControl.selectedSegmentIndex;
[_tableViewController.tableView reloadData];
}
4) In the table view controller you can now add the switch statement that you suggested to tableView:cellForRowAtIndexPath::
switch (self.selectedSegmentIndex)
{
[...]
}
Note that invoking reloadData in step 3 is a crude way to force the table view to update all cells with the correct images. There are other methods in UITableView that give you more fine-grained control over which cells you want updated (e.g. reloadSections:withRowAnimation:).

Related

Can't change UILabel's text from another View Controller

I have two view controllers, one with a text field and a 'Next' button and another one with just a label. The idea is that you fill in your name in the text field and click 'Next'. It should then switch to the other view and the label should show your name.
When I switch views however, the label is just empty. I am rather new to Objective-C and I'm hoping someone knows why this is happening :).
ViewController.m:
#interface ViewController () {
IBOutlet UILabel *label;
IBOutlet UITextField *textField;
}
-(IBAction)go:(id)sender
{
label.text = textField.text;
}
-(IBAction)remove:(id)sender {
[sender resignFirstResponder];
}
You said that you have two View Controllers. The first View Controller has an instance of UITextField, and a UIButton. Your second View Controller just has an instance of UILabel.
However, I noticed in your code that ViewController.m, which I am assuming is your first View Controller, has two IBOutlets, one for a UILabel and one for a UITextField, which doesn't make sense because the UILabel is supposed to be part of your second View Controller.
What you need to do is properly delete the IBOutlet for the UILabel from your first View Controller. Then, in your second View Controller, add the IBOutlet for it's UILabel.
Then, you need to implement the prepareForSegue method in your first View Controller and add a code statement like this:
[[segue destinationViewController]label].text = textField.text;
This way, when the segue is performed, you will successfully pass your first View Controller's text field's text to your second View Controller's label.

Static UITableView not fullscreen

For my registration form, I am currently using a UITableView which isn't fullscreen and I add cells programmatically through hardcoding the datasource methods. By the time the whole class got very complex and huge.
Pastebin link
The cells are custom and have a UILabel and a UITextfield. Now one of the cells should have a button instead of the textfield. This would make the whole thing more complex then it should be, in my opinion. So my thought was using the static feature of the tableview in the storyboard. But this requiers a UITableViewController, if I use one the TableView is always fullscreen. Is there a way to se the static feature without a fullscreen TableView??
If you have a fixed number of cells, the static table view controller is a good option. Instead of implementing the datasource methods, as you mentioned, you can include each input field as an IB outlet.
If you want a static table view controller that is not full-width, embed the table view controller inside a container view.
For example, create a new view controller, add a container view object w/ the desired width in this new view controller, and then connect your static table view controller to the container view.
Note that the static table view controller becomes a childViewController of the enclosing view controller. You can facilitate access to the textFields from the enclosing view controller w/ a weak property to the textFields w/in the child view controller.
- (UITextField *)surnameTextField
{
UITextField *textField;
// reference childController that is initiated via containerView
if ([[self.childViewControllers lastObject] isKindOfClass:[NameViewController class]])
{
NameViewController *nameVC = [self.childViewControllers lastObject];
textField = nameVC.surnameTextField;
}
return textField;
}
You do not need to use a UITableViewController. Just drag and drop a table view from the control palette onto a UIViewController in your storyboard. Size and position it how you want to and add any other controls to the UIViewController that you need.
In the property sheet for the UITableView set the content type to 'Static Cells' then define your cells how you want them.

How to get a reference to the view controller of a superview?

Is there a way to get a reference to the view controller of my superview?
There were several instances that I needed this on the past couple of months, but didn't know how to do it. I mean, if I have a custom button on a custom cell, and I wish to get a reference of the table view controller that controls the cell I`m currently in, is there a code snippet for that? Or is it something that I should just solve it by using better design patterns?
Thanks!
Your button should preferably not know about its superviews view controller.
However, if your button really needs to message objects that it shouldn't know the details about, you can use delegation to send the messages you want to the buttons delegate.
Create a MyButtonDelegate protocol and define the methods that everyone that conforms to that protocol need to implement (the callback). You can have optional methods as well.
Then add a property on the button #property (weak) id<MyButtonDelegate> so that any class of any kind can be set as the delegate as long as it conforms to your protocol.
Now the view controller can implement the MyButtonDelegate protocol and set itself as the delegate. The parts of the code that require knowledge about the view controller should be implemented in the delegate method (or methods).
The view can now send the protocol messages to its delegate (without knowing who or what it is) and the delegate can to the appropriate thing for that button. This way the same button could be reused because it doesn't depend on where it is used.
When I asked this question I was thinking of, in a situation where I have custom cells with buttons on them, how can the TableViewController know which cell's button was tapped.
More recently, reading the book "iOS Recipes", I got the solution:
-(IBAction)cellButtonTapped:(id)sender
{
NSLog(#"%s", __FUNCTION__);
UIButton *button = sender;
//Convert the tapped point to the tableView coordinate system
CGPoint correctedPoint = [button convertPoint:button.bounds.origin toView:self.tableView];
//Get the cell at that point
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:correctedPoint];
NSLog(#"Button tapped in row %d", indexPath.row);
}
Another solution, a bit more fragile (though simpler) would be:
- (IBAction)cellButtonTapped:(id)sender
{
// Go get the enclosing cell manually
UITableViewCell *parentCell = [[sender superview] superview];
NSIndexPath *pathForButton = [self.tableView indexPathForCell:parentCell];
}
And the most reusable one would be to add this method to a category of UITableView
- (NSIndexPath *)prp_indexPathForRowContainingView:(UIView *)view
{
CGPoint correctedPoint = [view convertPoint:view.bounds.origin toView:self];
return [self indexPathForRowAtPoint:correctedPoint];
}
And then, on your UITableViewController class, just use this:
- (IBAction)cellButtonTapped:(id)sender
{
NSIndexPath *pathForButton = [self.tableView indexPathForRowContainingView:sender];
}
If you know which class is the superview of your view controller, you can just iterate through the subviews array and typecheck for your superclass.
eg.
UIView *view;
for(tempView in self.subviews) {
if([tempView isKindOfClass:[SuperViewController class] ])
{
// you got the reference, do waht you want
}
}

Determine the last selected row when returning from detailView of UITableView

Ok I push a detailView when row is selected in the UITableView. In the detailView I set som data and pass that to the webservice. I want to retrieve some data either before the detailView is pushed or when it will disapear. Based on the data retrieved will I set an UIImage as an indicator.
I wonder how I can determine the indexpath.row the detailView got pushed for and use that for setting the UIImage. Is this possible?
I have a UISegmentedControl which loads two different datasources for the UITableView.
For testing I pass the indexpath.row when the detailView is pushed and callback to the UITableView class along with a boolean if the UIImage should be display.
But my problem is that indexpath.row is 0 when the UITableView loads.
And when changing datasource back and forth and passing the indexpath.row with it, the indicator will be display on wrong rows.
The two datasources for the table is:
if (index == 0) {
dictionary = [firstArray objectAtIndex:indexPath.row];
}
else {
dictionary = [secondArray objectAtIndex:indexPath.row];
}
I then set the indicator for that row (will be the UIImage later):
if (indexPath.row == selectedIndex) {
cell.indicator.text = #"Begin";
}
When the detailView is pushed in didSelectRowAtIndexPath i pass in the indexPath.row:
NSInteger rowIndex = indexPath.row;
[self.detailViewController initWithDetailsSelected:rowIndex:dictionary];
When the detailView is done the UITableView class receives the callback:
- (void)processAttendanceSuccessful:(BOOL)successful:(NSInteger)_selectedIndex{
self.selectedIndex = _selectedIndex;
}
So based on a boolean a UIImage should be display for the correct row in the UITableView.
Any suggestions how I can go about this. It must be quite common to set some value in a detailedView and display some data for a specific row in the UITableView, but having a rather hard time finding any good tutorials for this.
I only find how to pass data into the detailView, which I dont have any problem with.
Thanks in advance people.
Don't deselect the row in tableView:didSelectRowAtIndexPath:. When you return to this view controller, just use indexPathForSelectedRow method of the UITableView object.
But in the long run this makes little sense. Since the table view reuses those cells, you also need to remember the rows that have the image. You will probably have to maintain an array for this. In which case, you can either pass the array and the index path to the detail view controller and update them there or use delegate or notification mechanism to update the array from within the table view controller.
I would use an ivar of int, using negative values to represent null selection:
#interface MyController : UIViewController {
int lastSelection;
}
...
#end
#implementation MyController
...
- (void)initWithBlablabla{
...
lastSelection = -1;
}
...
#end
However, I think you should better re-think about your design referring to KVO programming guide. In general MVC patterns, a controller is not the right place to store the state of the model.

How to make a custom tableView cell accessory

I have not yet found any really good examples on how to do this. There is an image that I want to use as the accessory button and when I put it in and click on it doesn't work. So it looks correct but doesn't work...
Here is my code:
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
cell.accessoryView = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"TableView_Green_Disclosure.png"]];
So how do I get my UIImageView to call accessoryButtonTappedForRowWithIndexPath whenever it is tapped?
A thorough reading of accessoryView and accessoryType would reveal that they are mutually exclusive ways to customize a cell.
Setting the accessoryType will cause the table view delegate method to be called when it is tapped.
Setting the accessoryView will ignore the setting of accessoryType and give you something to display. If you want to receive a callback from the custom view you've put in place, it should be a control that is wired up to do so. (Or any view with a gesture recognizer.)
If you use a button, and set its action to accessoryTapped:, you will receive the button as the "sender" argument. You can walk up the view hierarchy until you find a table view cell, and then ask your table view what the indexPath of that cell is. This will then get you an index into your model objects and you be able to act on it appropriately.
Alternate to the button, you can enable interaction on the UIImageView above, and add a gesture recognizer to it.
To make the button actually do something, you'll need to implement - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath from UITableViewDelegate.
When an accessory button is tapped in a row, this method will be called and you'll have the chance to act appropriately using the passed in index path to determine which row's accessory was tapped.
Check the blog post hdr->cmdline for creating custom accessory view for UITableView.
The author used UIButton objects with images for custom accessory view.
To make use of the accessoryView - you would need to set the cell's accessoryType to UITableViewCellAccessoryNone deposit a UIButton (with associated image) into the cell and then wire it up to receive user touches. You might use something like the code below as the IBAction response to the cell's UIButton being touched:
- (IBAction) accessoryButtonPressed:(id) sender
{
NSUInteger pathInts[] = { 0,0 };
pathInts[1] = self.currentselectedrow; // ivar set when tableview row last selected
NSIndexPath* indexPath = [NSIndexPath indexPathWithIndexes:pathInts length:2];
[self tableView:mytableview accessoryButtonTappedForRowWithIndexPath:indexPath];
}
The UIButton would be wired to execute this glue code by way of a line inside your tableview's "cellForRowAtIndexPath:" function
[thecell setButtonTarget:self action:#selector(accessoryButtonPressed:)];
One thing I noticed is that the UIButton seems to want a 'swipe right' versus a simple 'tap' touch in order to trigger the event - but it could be my beta iOS that's the problem. Note that I had added a UIButton* object named 'cell_accessoryButton' to the Custom Cell source.
In the cell's source you'd support the 'setButtonTarget' call with code like this:
- (void) setButtonTarget:(MyViewController*)inTarget action:(SEL) inAction
{
[self.cell_accessoryButton addTarget: inTarget
action: (SEL) inAction
forControlEvents: UIControlEventTouchUpInside];
}
It's so much easier to just use the accessoryType reference and let iOS do the heavy lifting - but, if you want a custom graphic, etc - this is another path that works.