In the parent view controller, I add a child view controller when the view loads:
// In the parent
- (void) viewDidLoad
{
[super viewDidLoad];
self.tokenFieldViewController = [[TokenFieldViewController alloc] init];
[self addChildViewController:self.tokenFieldViewController];
[self.view addSubview:self.tokenFieldViewController.view];
self.tokenFieldViewController.view.frame = self.view.bounds;
[self.tokenFieldViewController didMoveToParentViewController:self];
}
In essence, this child view controller has a text field in its view:
// In the child
- (void) loadView
{
self.view = [[UIView alloc] init];
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(0,0,99,99)];
[self.view addSubview:self.textField];
}
When coded like this, the text field is not tappable. That is, tapping it does not put a blinking cursor into it. However, when I add the child view controller in the parent's viewDidAppear, the text field starts working. I would like to know how to fix it for viewDidLoad, because I need the lifecycle of the child to match that of the parent.
I would suspect that your only problem with the tapping is this line:
self.tokenFieldViewController.view.frame = self.view.bounds;
Between viewDidLoad and viewDidAppear there are a number of things going on. The biggest of which is the measurement of views and subviews. This happens between viewWillLoadSubviews and viewDidLoadSubviews. You should not count on valid sizes until after viewDidLoadSubviews.
Override viewDidLoadSubviews and put your line in it, like this:
- (void)viewDidLayoutSubviews {
self.tokenFieldViewController.view.frame = self.view.bounds;
}
Related
I am coding in xCode 4.3
This is my first application.
I have a UIViewController, company logo on top, then search bar and then UITableView in the middle (with product names) and bottom footer image. Now I want that once an item is clicked on UITableView, only TableView is replace with a view showing product details.
Right now I can replace the entire view with following code:
if (!self.prodDetailViewController_)
{
self.prodDetailViewController_ = [[PCS1ProdDetailViewController alloc] initWithNibName:#"PCS1ProdDetailViewController" bundle:[NSBundle mainBundle]];
}
[self presentModalViewController:prodDetailViewController_ animated:YES];
But it just increases my work, because I will have to redo the top bar and bottom bar (which remains same in entire application) in all my views.
Is there a way that I just change the element of my main UIViewController to UITableView.
Thanking you in anticipation.
I'm going to assume you're able to use iOS 5 features here. What you can do is implement a container view controller - much like UINavigationController, but with your own view layout so you can keep logo, search bar, etc. all in place and only transition between views in a part of your view.
I created a new container view controller named ViewController. It has a UIView outlet containerView which is set up in the .xib file, along with a top bar, search bar, and bottom bar (corresponding to the other views you describe in your application). It also has properties tableViewController and detailViewController. In its viewDidLoad implementation, it adds a TableViewController instance as a child view controller. When the table view is tapped, the view controller adds a DetailViewController instance as a child view controller and transitions to it. Tapping a button on the detail view transitions back to the table view, and removes the detail view controller as a child.
Here's my viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableViewController = [[TableViewController alloc] init];
self.tableViewController.delegate = self; // I implement a protocol TableViewControllerDelegate to know when row is tapped
// Fix for origin being 20 by default.
CGRect frame = self.tableViewController.view.frame;
frame.origin.y = 0.0f;
self.tableViewController.view.frame = frame;
[self addChildViewController:self.tableViewController];
[self.containerView addSubview:self.tableViewController.view];
[self.tableViewController didMoveToParentViewController:self];
}
I have a delegate method so that the container knows when a table row is tapped, and does the transition between the table view and the detail view. Its implementation follows:
- (void)tableViewController:(TableViewController *)tvc didSelectIndex:(NSInteger)index
{
self.detailViewController = [[DetailViewController alloc] init];
self.detailViewController.backButtonBlock = [self backButtonBlock]; // This block handles the transiton from detail back to table
CGRect detailStartingFrame = self.detailViewController.view.frame;
detailStartingFrame.origin.x = self.containerView.frame.size.width;
self.detailViewController.view.frame = detailStartingFrame;
[self addChildViewController:self.detailViewController];
[self transitionFromViewController:self.tableViewController
toViewController:self.detailViewController
duration:0.5
options:0
animations:^{
CGRect newTableFrame = self.tableViewController.view.frame;
newTableFrame.origin.x = (-1.0f * newTableFrame.size.width);
self.tableViewController.view.frame = newTableFrame;
[self.containerView addSubview:self.detailViewController.view];
CGRect newDetailFrame = self.detailViewController.view.frame;
newDetailFrame.origin.x = 0.0f;
self.detailViewController.view.frame = newDetailFrame;
} completion:^(BOOL finished) {
[self.detailViewController didMoveToParentViewController:self];
}];
}
As mentioned above, the detail view executes a block when tapping on a back button. I create this block in ViewController here:
- (GoBackButtonBlock)backButtonBlock
{
GoBackButtonBlock block = ^ {
[self.detailViewController willMoveToParentViewController:nil];
[self transitionFromViewController:self.detailViewController toViewController:self.tableViewController duration:0.5 options:0 animations:^{
CGRect newDetailFrame = self.detailViewController.view.frame;
newDetailFrame.origin.x = self.containerView.frame.size.width;
self.detailViewController.view.frame = newDetailFrame;
CGRect newTableFrame = self.tableViewController.view.frame;
newTableFrame.origin.x = 0.0f;
self.tableViewController.view.frame = newTableFrame;
} completion:^(BOOL finished) {
[self.detailViewController removeFromParentViewController];
[self.detailViewController.view removeFromSuperview];
}];
};
return [block copy];
}
That's about all there is to it. Be sure to read the "Implementing a Container View Controller" section of the UIViewController class reference for more details. Hope this helps!
Prepare all of your detail in a View, then add this view to the current screen. That's that.
in your "tableViewDidSelectRowAtIndex" method,On selection of row,just show the particular custom view (which contains details of selected row),and at that same time hide the table view,so u can have the view that u want. And make a back button on that custom view,and on that button action,hide your current view and show the tableview again.
i like to create a second starting screen in my app.
My Idea is to use the default.png and load an UIView with an fullscreen UIImageView inside.
In viewDidLoad i thought about placing a sleep option and after this load the real app screen.
But also when my function is called in viewDidLoad, nothing happens.
Seems my superview is empty...
Here is a piece of code:
if (self._pdfview == nil)
{
pdfview *videc = [[pdfview alloc]
initWithNibName:#"pdfview" bundle:nil];
self._pdfview = videc;
[pdfview release];
}
// get the view that's currently showing
UIView *currentView = self.view;
// get the the underlying UIWindow, or the view containing the current view
UIView *theWindow = [currentView superview];
theWindow is empty after this line so that might be the reason why the other view is not loaded.
So my question, how do i create a second starting screen ?
Or three starting screens, like in games when i like to mention another company.
If I understand correctly, your point is that when your function above is executed from viewDidLoad of some controller, theWindow is nil, so your new view (startscreen) is not added to it.
A few observations:
if theWindow is nil, then self.view is the topmost UIView; you can try and replace it, or simply add your view to it:
UIView *currentView = self.view;
// get the the underlying UIWindow, or the view containing the current view
UIView *theWindow = [currentView superview];
UIView *newView = _pdfview.view;
if (theWindow) {
[currentView removeFromSuperview];
[theWindow addSubview:newView];
} else {
self.view = newView; //-- or: [self.view addSubview:newView];
}
if you want to get the UIWindow of your app (which seems what you are trying to do), you can do:
[UIApplication sharedApplication].keyWindow;
and from there you can either set the rootViewController (from iOS 4.0)
[UIApplication sharedApplication].keyWindow.rootViewController = ...;
or add newView as a subview to it:
[[UIApplication sharedApplication].keyWindow addSubview:newView];
in the second case, you should possibly remove all subviews previously added to the UIWindow. (Iterate on keyWindow.subviews and call removeFromSuperview).
OLD ANSWER:
I think that you should try and add your pdfview as a subview to the current view:
[currentView addSubview:videc];
or to what you call theWindow:
[theWindow addSubview:pvidec];
and, please, move the release statement after the `addSubview, otherwise the view will be deallocated immediately.
Situation:
I'm trying to display a loading screen while waiting for my asynchronous connection to return with data to populate the tableview.
Problem:
Creating and adding the loadingscreen works fine, however, the tableview draws its lines over it, see screenshot:
.
Code: I add the view with these lines:
-(void) viewDidLoad{
[super viewDidLoad];
_loadScreen = [[LoadScreen alloc] initWithFrame:self.view.bounds];
[self.view addSubview: _loadScreen];
[self fetchRemoteData];
}
Question: Is it possible to add the loading view ontop of the table? Or can i make sure the tableview does not draw its lines untill i call reloadData?
-Thanks in advance,
W
I've done it like this many times:
-(void) viewDidLoad{
[super viewDidLoad];
_loadScreen = [[LoadScreen alloc] initWithFrame:self.view.bounds];
[self.tableView addSubview: _loadScreen];
self.tableView.hidden = YES;
[self fetchRemoteData];
}
- (void)dataFetchedSuccessfully
{
self.tableView.hidden = NO;
}
Just hide the tableview and show it again when the data has been loaded.
There is some approaches that will solve you problem:
-Set a footer view for the table view, so all lines should disappear.
self.tableView.tableFooterView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
-I assume that you use UItableViewController. If so self.view and self.tableView both represents the same view, so by setting:
self.tableView.hidden = NO;
It will hide even your loading view. What I encoure you to do is to create a custom view which has an table view as its subview. Then you can hide this table view by only showing an loading view.
Hope I could help.
I have a View Controller that initializes a UIView as its view. That view initializes another UIView as a subview. Both UIViews communicate with the View Controller through a delegate/protocol.
Each UIView creates an instance of the ViewController and makes it equal to the delegate:
ViewController *aDelegate = [[ViewController alloc] init];
self.delegate = aDelegate;
PROBLEM: The View Controller has a variable called (int)selection that is modified by both UIViews. Both views must know how each other modified the variable, but since each has a different instance of the View Controller that communication is impossible. How would I fix this problem?
Thanks a ton
EDIT: Peter mentioned assigning the delegate at the views creation which I like, but how would I do that for the subview since it is created in the UIView and not the View Controller. PS. In reality it is a subview of a subview of a subview so can I create them all in the View Controller and then assign it as the delegate?
Tried assigning the delegate as follows but it continually crashes when I attempt to call a ViewController method from the view:
MyView *mainView = [[MyView alloc] initWithFrame:frame];
self.view = mainView;
mainView.delegate = self;
[mainView release];
The views does not need to know about each other. In your view controller you define a property for the sub view
#property (nonatomic, retain) MyView *myView;
Then you create your sub view and assign the delegate. This can be done in viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
CGRect frame = ...;
MyView *subView = [[MyView alloc] initWithFrame:frame];
self.myView = subView;
subView.delegate = self;
[self.view addSubView:subView];
[subView release];
self.view.delegate = self;
}
Then in your delegate method, which I just guessed how it could look, you can update the view controller as well as the other view.
- (void)view:(MyView *)view didUpdateSelection:(int)newSelection {
self.selection = newSelection;
if (view == self.view) {
self.myView.selection = newSelection;
}
else {
self.view.selection = newSelection;
}
}
It sounds like instead of each view allocating a separate instance of the view controller, you want to assign the instance of the view controller that created the views as the delegate of each view.
One way to approach this is to have the view controller assign itself as the view's delegate when it creates the view.
I added a UITableView as a subview to a custom UIView class I'm working on. However I noticed that whenever I scroll the table it calls my classes layoutSubviews. I'm pretty sure its the UIScrollview that the table is inheriting from which is actually doing this but wanted to know if there is a way to disable this functionality and if not why is it happening? I don't understand why when you scroll a scrollview it needs its superview to layout its subviews.
Code:
#implementation CustomView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.clipsToBounds = YES;
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0, 15.0, 436.0, 132.0) style:UITableViewStylePlain];
tableView.dataSource = self;
tableView.delegate = self;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
tableView.backgroundColor = [UIColor clearColor];
tableView.showsVerticalScrollIndicator = NO;
tableView.contentInset = UIEdgeInsetsMake(kRowHeight, 0.0, kRowHeight, 0.0);
tableView.tag = componentIndex;
[self addSubview:tableView];
[tableView release];
}
return self;
}
- (void)layoutSubviews {
// This is called everytime I scroll the tableview
}
#end
Yes, a UIScrollView does call layoutsubviews whenever it scrolls. I could've sworn this was stated in the documentation somewhere, but I guess not.
Anyways, the prevailing idea for this is that a UIScrollView should layout its stuff so that views that currently can't be seen shouldn't be laid out. As users scroll in the scroll view, it should add and remove subviews as necessary. I'm guessing this is what TableViews use to enqueue table cells that get hidden.
Is there any reason why you would care if layoutsubviews gets called or not?
UITableView at least does appear to layout its superview. This behavior can be problematic when you have a layoutSubviews method that might be expensive (e.g. if you call some JavaScript).
The quick fix is add an intermediary subview that prevents the scroll view from laying out your superview. Instead, it will layout the intermediate subview.
This could be somewhat imperfect but it should work for most cases:
Assume UIView * intermediateView is defined as an instance variable:
-(id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame: frame];
if (self)
{
UIScrollView * theScrollView; // = your scroll view or table view
intermediateView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
// Ensures your intermediate view will resize its subviews.
intermediateView.autoresizesSubviews = YES;
// Ensure when the intermediate view is resized that the scroll view
// is given identical height and width.
theScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[intermediateView addSubview: theScrollView];
// Ensure the frame of the scroll view is exactly the bounds of your
// intermediate view.
theScrollView.frame = bottomContainerView.bounds;
[self addSubview: intermediateView];
}
return self;
}
-(void) layoutSubviews
{
intermediateView.frame = CGRectMake(0, 50, 42, 42); // replace with your logic
}
Not sure i understand your issue correctly but when you scroll a tableview it removes the cells not shown from the memory and loads them again when they are scrolled back into visibility (cells are allocated on demand, only the visible ones) , in effect doing what you seem to be describing.