I have a cell with a few lines of text which changes colour when selected/highlighted. The problem is that as the new viewController is being pushed, the deselection animation occurs which is visually distracting as the text suddenly reverts to the unselected state. I have moved the deselectRowAtIndexPath statement after the line where the new view controller is pushed, but this has no effect.
How can I prevent the user seeing the cell deselection (without implementing a timer)? Any help would be appreciated please.
If you're using a UITableViewController, you won't need to call deselectRowAtIndexPath:. This will be done for you automatically when your table view becomes visible again.
If you're not using a UITableViewController because you have a more complicated interface, you would need to call deselectRowAtIndexPath: manually from the viewWillAppear:animated: method.
I think the general paradigm used with table views and pushing new VCs is that you deselect the table row in your viewWillAppear:animated method. Then as the VC is popped, they see which row had been used to navigate to that VC.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.myTableView deselectRowAtIndexPath:[myTableView indexPathForSelectedRow] animated:YES];
}
so remove deselectRow from your didSelectRowAtIndexPath method.
Related
So I have two search bars, a couple of buttons and a tableView below those (yes, all these) in one view. And I also have a navigation bar on top of it all with a back button.
For a particular operation, I remove all search bars, buttons and I display only one uitableviewcell in my view.
If I press edit, I want the whole view to be reloaded, not just the tableView but I want the view to have the search bars and buttons and the navigation screen.
I did [self.view reloadInputViews] in the IBAction of the edit button. Control goes here, but the view is just not reloaded. Why?
reloadInputViews is specifically used for views that are firstResponders. Probably self.view is not the first responder at that time. But why you want to do all this using "reloadInputViews", it wouldn't be easier to use: [self.view setNeedsLayout] ?
A better way to handle refreshing the state of a view is to abstract the configuration of your UIViewController's view's subviews into a custom method that you can call initially from viewDidLoad and again as an action of your UIbarButtonItem.
- (void)viewDidLoad {
//Specific, non repeated view setup
[self resetViewState];
}
- (void)resetViewState {
//Repeatable view setup
}
The reason it's worth making the distinction is because viewDidLoad is called automatically by the system after the view is lazy loaded. By design it's not a method that should be called multiple times per view. It will be called again if the view is discarded and recreated.
You can also move the layout of your UIViewController's subviews into viewWillLayoutSubviews if you need to do layout work, again this gets called by the system at various times.
I do not know how I get the answer myself right after posting it here on StackOverflow. Sorry for the trouble guys (sigh!)
I did [self viewDidLoad];
on the main screen on my project I have a a TableView, with a navigationBar on top and toolBar on the bottom. The thing is, one of the buttons in the toolbar needs to slide up an UISegmentedControl from the toolbar. I have already implemented this control in a custom UIView, BUT, if i just add it to my rootviewcontroller.view and slide it into place, it will scroll with the rest of the table, which is undesired (I would like it to appear as an extension of the toolbar).
So, what do I do? In rootViewController I do
self.filterView = [[FilterView alloc] initWithTarget:self.tableView reloadDataSelector:#selector(reloadData)];
[[self.view superview] addSubview:self.filterView];
[[self.view superview] bringSubviewToFront:self.filterView];
I add the control view (self.filterView) to my view's superview, and that puts it above the tableview's scroll.
BUT, now the problem. As soon as the tableView goes out of view (I push another view on the navigationController, or specially if the app goes to background) this view gets re layed-out, and my controller view gets moved to (0,0).
The thing is, as far as pushing new views on the navigationController, I can kind of control it by repositioning it in viewWillAppear and viewWillDisappear in my rootViewController. But when the app goes to background and comes back those functions don't get called.
So, is there any way to
(a)prevent my controller view from being moved
(b)detecting when it has been moved unintentionally
(c)detecting coming and going from background from rootViewController
???
I know I can detect passes to background in appDelegate, but I wouldn't feel comfortable dealing with layout issues there.
Thanks!!
EDIT:
To add some info, if I do
NSLog(#"%#",[self.view superview]);
//I get <UIViewControllerWrapperView: 0x61589d0; frame = (0 64; 320 372); autoresize = W+H; layer = <CALayer: 0x615d9c0>>
EDIT2: I guess I could create my own wrapper UIView first, load all my current view hierarchy in it, and put the controller view there. Do you guys think that would be worth the trouble? isn't there a simpler way?
EDIT3: Ended up opting for changing my rootViewController from UITableViewController to UIViewController, and added the tableView programatically, as Phil suggested below. I can now control my main view as I like, and since I am putting my segmentedControl view there I can control how it is positioned, as opposed to before, when I was placing it in an UIViewControllerWrapperView, which I am not too sure who controls or what it does to it's subviews.
SO, just out of curiosity, does anyone know why the UIViewControllerWrapperView that was wrapping my UITableViewController's view was moving my UIView on coming back from background??
To clarify, the setup was like so:
UIViewControllerWrapperView
|
|UITableView
|Custom SegmentedControl UIView
As a side note, you have a pattern in your code that looks like that view is going to leak and addSubview will automatically put the view on the top of the view order.
However, the reason your view is scrolling is because it is being added as a subview of the UITableView, which is a subclass of UIScrollView. When the scroll view scrolls, it will move any subviews up or down by the contentOffset property. As the UIScrollView scrolls it will repeatedly layout its subviews. Since the table view isn't aware of your custom subview, it appears it is just moving it to the 0,0.
I assume you are using UITableViewController. If you plan to have more than just a table view for this view controller, then you should implement a standard view controller instead. This controller would have a normal view that contains the tableview and your other views. UITableViewController is merely for convenience for a very common case.
It's very easy to duplicate UITableViewController's functionality if you are worried about that. It is actually very clearly documented.
When the table view is about to appear
the first time it’s loaded, the
table-view controller reloads the
table view’s data. It also clears its
selection (with or without animation,
depending on the request) every time
the table view is displayed. The
UITableViewController class implements
this in the superclass method
viewWillAppear:. You can disable this
behavior by changing the value in the
clearsSelectionOnViewWillAppear
property.
In your implementation:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if ([self.tableView numberOfSections] == 0) {
[self.tableView reloadData];
} else {
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];
}
}
When the table view has appeared, the
controller flashes the table view’s
scroll indicators. The
UITableViewController class implements
this in the superclass method
viewDidAppear:.
In your implementation:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.tableView flashScrollIndicators];
}
It implements the superclass method
setEditing:animated: so that if a user
taps an Edit|Done button in the
navigation bar, the controller toggles
the edit mode of the table.
In your implementation:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self.tableView setEditing:editing animated:animated];
}
I have an app I am working on, that has a table view with a few cells, and when you hit a cell, the nav controller pushes a new view. Pretty simple stuff. The problem is that when I hit on a row, the whole row highlights blue, and then remains blue all while the view is being presented by the controller, and then if I hit back to go back to the table view, the row is just still completely highlighted Even though I'm not pressing it or anything.
I have made a few apps with the table view before, and have never seen this behavior before, so I am at a loss as to what is going on. Is there some setting that I have set wrong somewhere?
Thanks
You need to deselect cell manually (probably in tableView:didSelectRowAtIndexPath: method):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Your code
...
}
If your view controller is a subclass of UITableViewController, it will automatically deselect the row after you hit the back button. If you don't subclass UITableViewController, you need to call deselectRowAtIndexPath:animated: when you want it deselected.
I have a UITableView on a view. This UITableView has cells which are made up of a checkbox custom control, a label and a disclosure accessory. When I select a row in the tableview it selects correctly (blue highlight shows 100% correctly).
I then created a didSelectRowAtIndexPath delegate to push a detail view controller onto the navigation stack using the following code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyDetailsViewController *detailsViewController = [[MyDetailsViewController alloc] initWithNibName:#"MyDetailsView" bundle:nil];
[[self navigationController] pushViewController:detailsViewController animated:YES];
[detailsViewController release];
}
This works - in so far as that it pushes the view controller onto the navigation stack and presents the view. The problem I'm having is that the animation is really "flickery" and the row that was selected doesn't even get highlighted before the animation starts.
I've done this before (but never with custom cells) and I know that the procedure should be as follows: row highlights, detail view animates in, (edit details), detail view animates out, row gets deselected.
I'm not sure why my row automatically gets deselected (or de-highlighted) when I select it and why the animation is flickery.
Has anyone had this before?
Ugh, I so hate it when I find the answer to my own question about a minute after posting it. The problem was that I was doing all kinds of things in viewWillDisappear that was modifying my table data. ****grin****
I have a navController and tableViewController set up such that selecting a table row will push a detail view on the navController's stack and display detailed information about the row selected. In the detail view I use a single UILabel to display the info, and I set the value of the label's text property in viewDidLoad of the detail view controller.
The first time I select a row, the detail view comes up with the expected text. When I go back to the table view and select a different row, the detail view comes up with the same text as the first time.
I have seen some code samples where the detail view controller is released and then set to nil after being pushed on the navController's stack. If I add that to my code, the problem goes away, but I don't understand why.
Can someone explain to me what's going on here?
Thanks!
-viewDidLoad is called only when the... well, when the view is loaded. That is to say, when it is created in memory, which is the first time you create the view controller. Any customizations based input data should be done in -viewWillAppear: instead, which gets called every time before you push it onto the navigation controller.
Although, in general practice, I always release a new view controller immediately after pushing it onto the stack, since it doesn't belong to me any more, it belongs to the navigation controller. In this case, the next time you push it on to the stack, it will load the view again, since it's a new object.
- (void) tableView:(TableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController *screen = [[MyNewViewController alloc] initWithData:[data objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:screen animated:YES];
[screen release];
}
The main idea, though, is that customizing a view based on data that may change every time you see the view should be done within -viewWillAppear:. Customizing a view further than you can in Interface Builder, changing things which won't change no matter what data you're looking at, should be done in -viewDidLoad