Relatively basic issue, but I spent whole day figuring out without any success. I have ViewController with main fullscreen View and separate TableViewController with TableView which does not cover the entire screen. I need to use UIRefreshControl on the TableView, hence the separate TVC.
I want to achieve that on cell selection existing TableView will flip around to be replaced with another TableView (like a step 2 of user selection). I expect that the existing main View will be visible during the TableViews transition.
I was able to achieve the full View flip transiton into different view using the begin animation/commit animation method, but never the childview only, nor using the new animation block method. Can you please suggest the animation code and how should I introduce the second tableview (under the existing TVC or under it's own TVC)?
#implementation ViewController
TableViewController *tableVC;
- (void)viewDidLoad
{
[super viewDidLoad];
tableVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TableVC"];
[self addChildViewController:tableVC];
tableVC.view.frame = CGRectMake(8, 177, 304, 371);
[self.view addSubview:tableVC.view];
}
Many thanks.
See this Sample code it might help you
https://github.com/GrioSF/spincard
Related
This is the first time I have tried to do this and I understand the concept of what I am trying to do but just not 100% sure on how to execute it.
I have a main view and a sub view.. the main view has several custom uitableviewcells each with a single textlabel. when the cell is selected it loads the subview onto the navigational stack, their is a list of standard uitableviews that I have loaded with values from the database.
When you select one of these uitableviewcells in the sub view, I want to capture that value pop the current view from the stack and then load the selected value of the subview into the main tableviews cell that was originally selected..
In the sub view I am thinking something along the lines of
MainViewController *mvc = [[MainViewController alloc] initWithNibName:#"MainViewController" objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:mvc animated:YES];
[mvc release];
But this obviously this isnt passing any values around...
You want to use delegation, if you don't know how to use it, this link might help:
http://cocoadevcentral.com/articles/000075.php
So the general idea is that your main view will be the delegate of your subview, and the subview will tell it's delegate when a cell was tapped and pass in that variable (for example: [delegate cellChanged:cell];), then the main view (the delegate of the subview) will handle that.
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 wanted to add a view to the bottom of my screen. The controller is a UITableViewController, how do I shrink the tableView and add a extra view at the bottom of the tableview?
I've tried setting the frame of self.tableView in different places (viewDidLoad, viewWillAppear etc) but nothing happens. The tableView is created by IB and not programtically.
I've tried added a footer to my table view but that's not what I want, because the footer actually scrolls up, I want a static non moving View at the bottom of the screen.
I'm not saying you can't do it otherwise, but you may not want a UITableViewController for this situation. You can still have your view controller implement UITableViewDelegate and UITableViewDataSource, but place a vanilla UIView in your nib, into which you place a UITableView. Then just make sure to set the view outlet to the UIView containing your table. This has the effect of allowing you to create your additional view within IB. I just tried this and it appeared to work.
I'm guessing you're using a UINavigationController. When you push a controller onto your navigation stack, UINavigationController resizes its view to full screen, ignoring the geometry and autoresizing behavior you've defined in IB.
This resizing seems to happen after viewWillAppear:. In the past I've had some success resizing a table view and adding a sibling view in viewDidAppear:, after calling [super viewDidAppear:]. This is a bit risky though, since Apple could break it by changing how UINavigationController works behind the scenes.
A safer option is to push a view controller onto your navigation stack that controls a wrapper view. Then add your UITableView and its sibling as subviews of that wrapper view. The annoying thing about this option is that you'll probably want to use a nested UITableViewController to manage your non-full screen table view, but the documentation for UIViewController says it's designed to manage full screen views only. If you decide to ignore this admonition and nest your view controllers anyway, you'll find that viewWill/DidAppear/Disappear don't get called on the nested controller, so you'll have to manually delegate those methods from your wrapper view controller. This lack of support for nested controllers is one of my biggest pet peeves about UIKit, and I've gone to great lengths to engineer around it.
If you want to toe the line and use view controllers only for full screen views, you can push a normal view controller that controls your full screen wrapper view, manually implement all the UITableViewDataSource and UITableViewDelegate methods in your view controller, and set it as the delegate for your table view.
you want to change the -loadView method. Not viewDidLoad or viewWillAppear. This will allow you to make additional configurations with your tableview even if it is created in IB.
- (void)loadView {
[super loadView];
CGRect titleRect = CGRectMake(0, 0, 300, 40);
UILabel *tableTitle = [[UILabel alloc] initWithFrame:titleRect];
tableTitle.textColor = [UIColor blueColor];
tableTitle.backgroundColor = [self.tableView backgroundColor];
tableTitle.opaque = YES;
tableTitle.font = [UIFont boldSystemFontOfSize:18];
tableTitle.text = [curTrail objectForKey:#"Name"];
self.tableView.tableHeaderView = tableTitle;
[self.tableView reloadData];
[tableTitle release];
}
I don't know how to do it in IB but the way to do it in code is with this:
- (void) loadView
{
UITableView *tv = [[UITableView alloc] initWithFrame: rect
style: UITableViewStyleGrouped];
// finishg configuring table view
self.view = tv;
[tv release];
}
Trying to do it in two stages -- style first and then frame or frame first and then style -- neither of them works.
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.
I think I'm missing something fundamental and so I want to ask the community for some help. I'm building an app based around a basic iPhone Utility Application. My MainView and FlipsideView share some elements so I have created separate ViewControllers and nib files for those pieces. In order to do this I have done the following:
1. Created a viewcontroller called searchDateViewController which is the file's owner of searchDateView.xib
2. searchDateView.xib is basically a UIView with a UILabel inside, the view is wired up correctly
3. Inside both MainViewController.m and FlipsideViewController.m I add a subview as folllows:
- (void)loadView{
[super loadView];
searchDateViewController = [[SearchDateViewController alloc] initWithNibName:#"SearchDateView" bundle:nil];
[[searchDateViewController view] setFrame:[searchDateView frame]];
[[self view] addSubview:[searchDateViewController view]];
...
}
Everything displays and works just fine. Basically depending on actions that happen in each of the main and flipside views the UILabel of the nib is changed. However, I wanted to do something slightly different if the searchDateViewController is loaded from the MainView or the FlipsideView. However, I can't seem to figure out which ViewController is adding the searchDateViewController subview.
In searchDateViewController I tried:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"superview %#", self.view.superview);
NSLog(#"parentviewcontroller %#", self.parentViewController);
}
In both cases I get nil.
So my question is - can I find out which ViewController is adding searchDateViewController a a subview? If so how? Or if my logic here is completely messed up, how should I be doing this?
Thanks!
viewDidLoad is invoked when the view controller has loaded its view. In your case, that happends in this line:
[[searchDateViewController view] setFrame:[searchDateView frame]];
At that moment, you haven't yet called addSubview: so it is no wonder the view's superview is nil.
To solve your problem, you should define a property inside SearchDateViewController to distinguish between the different cases. This property would then be set accordingly by the parent controller that creates the SearchDateViewController instance.
Generally, I do not think it is a good idea to use a UIViewController subclass as a controller for a view that is used as a subview of one or several fullscreen views rather than be used as a fullscreen view itself. Much of UIViewController's logic works on the assumption that it is used to manage a fullscreen view. For instance, with your design, I think it's possible that SearchDateViewController will modify the view's frame when the device orientation changes etc. Since you don't need all this functionality for a non-fullscreen subview, I suggest you subclass your SearchDateViewController directly from NSObject.
ViewController and views are completely separate.
In most cases, when you add a subview to a parent view you don't add its controller to the parent's viewController. The exception to this rule is the navigation controller which adds the controller instead of the view to maintain a hierarchy of view controllers.
Your SearchDate viewController can't find a parent controller because you never assigned one and the system does not do it automatically. You can just assign a parent controller when you evoke the view from another controller.
searchDateViewController.parentController=self;