how to make a custom scroll container view controller? - ios

There are two view controllers in my app, e.g, vc1 and vc2. The two view controllers are as the subviews of a scrollView, so the user can scroll the screen to switch the view. However, the simple implement has a problem: the viewWillAppear method of vc1 and vc2 is called only once. so I want to implement my scroll container view controller, which can call viewWillAppear method correctly, please tell me how to implement it.

I am not sure what you are trying to do, but I think a simple UITableView or UICollectionView may be better for you because they have datasource method that will automatically called when a view will show up in the screen. You can update your two views when you need to return a UITableViewCell or UICollectionViewCell.

I'm not sure if this will work, but I'm thinking you can check if the vc1 and vc2's frames are withing the screen's bounds in the delegate method of the scrollView.
I'm pretty sure there's a method being called every time the scrollView is being scrolled. In this method, you can check
//put this in your .h or something
BOOL vc1IsVisible = true;
//in the scrollView delegate-method that is called upon scrolling
if([self isInsideView:vc1])
{
if(!vc1IsVisible)
{
vc1IsVisible = true;
[vc1 viewDidAppear:NO]; //or whatever it is for animation
}
}
else
{
if(vc1IsVisible)
vc1IsVisible = false
//and viewDidDisappear?
}
and then create a method somewhere like this
-(BOOL)isInsideView:(UIViewController*)vc
{
//Check if vc.origin.y is greater than scrollView.size.height or something maybe?
//You can probably also try using the scrollView's contentOffset and use that
//relative to the viewController's sizes.
//if the viewControllers bounds are withing the scrolls bounds, return YES;
//else, return NO;
}
Sorry I can't really test anything just now. Maybe I'll make something and update the answer later if you haven't figured it out. And you need to do it with both. I'm sure you can figure out a better way to include both in one method with this, or even with one variable.

Since you are using ViewController by adding it subview of scrollview, by adding ViewController this way viewDidLoad, viewWillAppear, viewDidAppear will be called only once, I mean there is no use of viewWillAppear here as such, rather if you want to update anything in the added ViewController you should create a public class in ViewController and call it when you need an update..

Related

UIView Animation Fails

So, I want to do some basic animations of labels and later views.
I have a label, I'm trying to get it to move when a view loads, so I call the following method at the end of viewDidLoad:
- (void)animateView {
NSLog(#"animateView");
[UIView animateWithDuration:20 animations:^{
// set new position of label which it will animate to
self.dcFirstRunDaysLabel.frame = CGRectMake(20,320,280,215);
}];
}
Instead of animating, the label appears in position.
I've tried every tutorial and read through the docs. I get no errors.
Any thoughts?
Cheers.
Try calling your animateView method in viewDidAppear. Because in viewDidLoad your view isn't visible yet.
viewDidLoad:
Called after the controller’s view is loaded into memory.
viewDidAppear:
Notifies the view controller that its view was added to a view hierarchy.

Where and when to initialize/create view programatically

I had this question when/where to create and initialize views that are created programatically, so I hope some discussions here will shed more light on this topic for me.
This slide:
says: "not to initialize something based on the geometry of the view in viewDidLoad" and suggests viewDidAppear.
Imagine my view controller has view. I want to add 10 dynamic UIButtons to it.
Shall I put the code like below to the viewDidAppear?
-(void) viewDidAppear:(BOOL)animated
{
...
UIButton *button1 = [[UIButton alloc] initWithFrame: rect1];
[self.view addSubview: button1];
UIButton *button2 = [[UIButton alloc] initWithFrame: rect2];
[self.view addSubview: button2];
...
}
But this creates the buttons each time the view is shown. Is it what we want?
On the other hand if I put the code in viewDidLoad slide suggest not to initialize geometry of these views there.
Or shall we create buttons in viewDidLoad and set their frames in viewDidAppear?
What approach do you usually take?
But this creates the buttons each time the view is shown. It's true.
So the best thing you can do is to add a boolean (lets name it isLaunched). You set it to FALSE in the method -(void)viewDidLoad
Then add a if condition in your -(void)viewDidAppear where you perform creation of buttons (or other stuff) and set the boolean to true at the end.
You should have something like that :
-(void)viewDidLoad
{
//some settings
isLaunched = FALSE;
}
-(void)viewDidAppear:(BOOL)animated
{
if(!isLaunched)
{
//creating and adding buttons
isLaunched = TRUE;
}
}
zbMax (and now Amar) offered good solutions to implement the view creations in viewDidAppear: I will provide the rational for doing this (over viewDidLoad).
It is pretty simple actually. In viewDidLoad none of the views are actually setup yet, so any attempt to set/create frames or bounds will be extremely inconsistent. Struts and springs (or autolayout) will take effect after this method which will create additional changes to your views. viewDidAppear: is the correct place to do this because you can now rely on existing views and setting frames.
Reason for not playing with the geometry in viewDidLoad is because view is still in the memory and not on the window. Once the view is put on the window, then you can specify geometry. That happens when viewDidAppear is called for your controller.
As recommended, you should do all the initialisation in viewDidLoad as this is one time task and need not be repeated. Hold references to the added subviews and give them appropriate frame in viewDidAppear.
When you are dealing with custom UIView and its subviews, layoutSubviews is the method you need to override in the custom view in order to rearrange the geometry of its subviews.
Hope that helps!

Invalidate a timer that belongs to a UIView subclass

I have a repeating timer that belongs to a UIView subclass.
The class has a nib that loads it and I'm using ARC.
I'd like to invalidate the timer when the UIView is either...
Removed from its superview
The ViewController that contains its superView is popped off the stack.
I can't seem to find a method like viewDidDisappear on UIView.
Is there any other way to intercept this?
At the moment, after the ViewController is popped the timer keeps firing and creating NSLog outputs.
For the view controller being popped: just use viewDidDisappear or similar. There's also UINavigationControllerDelegate that may be useful.
For the view itself: have you tried using willMoveToSuperview: method in UIView? I haven't verified this, but in theory the view will move to superview nil when it is removed from its superview.
So try the following in your view:
- (void)willMoveToSuperview:(UIView *)superview {
if (!superview) {
// cancel timers
}
}
There's also a willRemoveSubview: method, but that would get called on the superview rather than the view being removed.
Have you tried invalidating it in the dealloc

dismissModalViewControllerAnimated resets contentOffset

I have a problem with my table view. When dismissing a modal view controller presented on top of it, it always scrolling to the top . I have tried observing the changes to contentOffset using KVO, but the one that messes my view goes behind it.
From the UITableViewController, when user finishes his task in the modal dialog, self.tableView.contentOffset is , I call:
[self dismissModalViewControllerAnimated:YES]
Subsequently, when the viewWillAppear:(BOOL)animated is called, the self.tableView.contentOffset is already set to 0,0.
Is this supposed to be happening? I am able to work around the issue by remembering the scroll position before presenting the modal view and restore it back in viewWillAppear after dismissing the modal view. But it seems wrong. Am I missing something?
I have found similar problem described in Dismiss modal view changes underlying UIScrollView.
It looks like this is default behavior of UITableViewController. I tested it in very simple app and It worked exactly as you said. If you don't like it, use UIViewController instead.
Here is how I work around this problem, so that the table view maintains the original scroll position. In my subclass of UITableViewController I have added:
#property (assign) CGPoint lastScrollPosition;
Then in the implementation, I have overridden the following:
- (void)viewWillAppear:(BOOL)animated
{
self.tableView.contentOffset = self.lastScrollPosition;
}
- (void)dismissModalViewControllerAnimated:(BOOL)animated
{
self.lastScrollPosition = self.tableView.contentOffset;
[super dismissModalViewControllerAnimated:animated];
}
If you want your table to initially appear scrolled to non-zero position, as I did, don't forget to initialize the lastScrollPosition in your viewDidLoad.

Accessing parent view of current view - iOS

When I want to access the parent UIView of current UIView I declare object of parent UIView in current UIView and access it by assigning the parent UIView object to current view's object property.
Is there any way to get rid of this and directly call the parent view's methods or properties?
I also tried (parentView *) self.view.superview but didn't help.
It gives error while calling function for this object as
[UIVIew unrecognized selector......
If you're calling this directly from a UIView (and not a UIViewController), it should be self.superview instead of self.view.superview.
# Naveed: This is the common code u can use to any view whether it is parent or child view, Just change button name which u want to press and the view name on which u want to go. For example on back button press u want to go on library view then write this code -
-(IBAction)backButtonPressed:(id) sender
{
llibraryView *libraryview=[[libraryView alloc] initWithNibName:nil bundle:nil];
libraryview.modalTransitionStyle=UIModalTransitionStyleCoverVertical;
[self presentModalViewController:libraryview animated:YES];
[libraryview release];
}
Let me know whether ur problem is solved or not.
What I can think is that you want to call the viewcontroller's method from your view, which you added to the viewcontroller's view.
Now you have two options from here, either you set your view controller your view's delegate and then call your viewcontroller's method by [delegate performSelector:] approach.
The other approach is you access your view's superview, and then get it's controller. You can not do that directly, coz if you could do that it would defeat the entire purpose of MVC.
But, still there is a way out, here you go:-
Get to UIViewController from UIView?
Hope, this helps you.

Resources