How to actually implement the paging UIScrollView from Apple's example? - iphone

I found an example of a paging UIScrollView in Apple's developer docs and it's just what I want for my application. I have a main view with a "Help" button that I want to present a view that users can page through to see all the help topics. The sample is at:
https://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/UIScrollView_pg/ScrollViewPagingMode/ScrollViewPagingMode.html%23//apple_ref/doc/uid/TP40008179-CH3-SW1
And it works as advertised, but, as with so many of Apple's examples, they do all kinds of code right in the AppDelegate. I have my own AppDelegate, but then I have a NavigationController with a RootView that has this "Help" button on it. I can't seem to get the example code integrated into my own and have it work. I'm pretty sure I can't put the code they have in their AppDelegate in my own, but how to set it up eludes me.
Can someone point me to an example where they do what I'm talking about?
EDIT: I was able to create a new project and get it to work like Apple's by moving all the AppDelegate methods into the UIViewController that the template supplied and creating a new ContentPage UIViewController to hold the content. It can scroll from page to page, so I think I can insert this code into my other project ok.
I replaced the applicationDidFinishLaunching with an equivalent viewDidLoad and got rid of the AppDelegate stuff dealing with window and such. Then I changed Apple's initWithPageNumber: method to refer to my help pages rather than just creating instances of their generic views.
- (id)initWithPageNumber:(int)page {
NSLog(#"--initWithPageNumber:ContentPage");
if (self = [super initWithNibName:[NSString stringWithFormat:#"HelpPage%d", page] bundle:nil]) {
pageNumber = page;
}
return self;
}
Thanks for the help - sometimes it's good just to have someone tell you it can be done to keep going!

In the sample code, the applicationDidFinishLaunching: method sets up the UIScrollView (and a UIPageControl, but let's ignore that for now) and then "loads" the first two pages. "Loading" a page consists of loading that page's view controller into memory and caching it. It then adds that controller's view as a subview to the UIScrollView offset with an appropriate x based on what "page number" it is. You should be able to do all that in your viewDidLoad.
If you have a blank view when your view controller's view is shown, then you haven't added the subview correctly to your UIScrollView. Depending on what exactly you changed, that could be some combination of (a) not getting the x offset correct, (b) not setting the frame correctly, or (c) not adding it as a subview correctly.
If you post some of your own code we might be able to shed some light on the problem.

Related

Connect action of a XIB loaded from a custom class to a storyboard

I'm trying to create a reusable component to display some photo collection.
The basic flow is the following :
First view : View. It contains my so called library, designed programmatically and loaded from storyboard by assigning a custom class
I take a photo in a modal view, openend from the 'take picture' button
Once the photo is saved on disk, I ask PhotoLib to create a new PhotoCell from the photo path
I would like my PhotoCell to be touch enabled so when I tap it, it opens the second view in a modal way, but from what I read I cannot do this from my PhotoCell or the UIImageView inside (not a controller).
So how can I do ? View is embedded in a NavigationController, even if not shown in the screenshots below.
Thank you !
If you create Photocell in photolib, then photolib should implementing delegate methods from photocell. But photolib itself is not rootviewcontroller, so it should declare delegate methods itself, and the containing view should implement it.
Basically you pass Photocell from itself to Photolib (which implements delegate method
-(void) openPhotoCell:(Photocell*)cell
{
[self.delegate openPhotocell:(Photocell*)cell];
}
, then it passes it to View, which in its turn opens it.
It may seem like pulling a tooth from an ear, but actually it's quite working and if you write good self-explanatory code, it's not a problem. I'm currently working on some big project with tens views and controllers and it works pretty good and nobody has problem with that.
If you have more layers, then maybe you should look into NSNotification.
Hope it helped, I'd be glad to explain more.
UPD:
Links:
about delegates in cocoa fundamentals guide
delegation pattern in wikipedia

Refreshing the content of TabView

Ok I am trying to refresh the tab content of each of my tabs after a web call has been made, and I have tried soo many different methods to do this that I have lost count. Could someone please tell me how this is possible?
The web call just calls JSON from a server and uses it to update the content of the tabs. For testing purposes I have a button set up inside my settings class. Settings class is a view within the home tab which has a button called refresh. When clicked this takes JSON stored on the device which is different to the one called from the web call on application start up. This saves me having to change the JSON on the server.
I will take you through some of the techniques I have tried and would be grateful if someone could tell me what I am doing wrong.
I tried making an instance of the class and calling the refresh method like this
DashboardVC *db = [[DashboardVC alloc] init];
[db refreshMe];
The refresh method in dashboard class is this
-(void) refreshMe
{
[self loadView];
[self viewDidLoad];
}
However no luck. This method will work if I call it inside the Dashboard class, but wont work if I call it from another class. I think it is become I am instantiating a new class and calling refresh on that. So I dropped that technique and moved onto the next method
This loops through all the tabBars and changes the tabTitles without any issues, so it I know it is definitely looping through the ViewControllers properly.
I also tried every varient of the view methods like ViewDidAppear, viewWillAppear etc, no luck.
I also tried accessing the refreshMe method I made in the dashBoard class through the tabController like this
[[[self.tabBarController viewControllers] objectAtIndex:0] refreshMe];
But again no luck, this just causes my application to crash.
I read through this guide
https://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewControllerPGforiOSLegacy/TabBarControllers/TabBarControllers.html
on the apple website but it doesn't seem to cover how to refresh individual tab content.
All I want is to have each individual tab refresh its content after the web call is made, and have spent ages trying to figure this out, but nothing is working.
So would be very grateful if someone could show me what I am doing wrong?
Thanx in advance....
EDIT:
Expand on what I have tried
After discussion with Michael I realised you should never call loadView as against Apple guidelines. So I removed any references to LoadView. I have now placed a method in all the main ViewControllers called RefreshMe which sets up the views, images texts etc in the class. And this method is placed inside the ViewDidLoad. Now I want to be able to call these methods after a web call has taken place, so effectively refreshing the application.
My viewDidLoad now looks like this in all my the main classes.
- (void) viewDidLoad {
[super viewDidLoad];
[self refreshMe];
}
And then the refreshMe method contains the code which sets up the screen.
The JSON data pulled from the web call will set up the content of each of the 5 tabs, so need them all to be refreshed after web call.
I tried looping through the viewControllers and calling viewDidLoad, which should in turn call the refreshMe method which sets up the class, however nothing happens. Code I used was this
NSArray * tabBarViewControllers = [self.tabBarController viewControllers];
for(UIViewController * viewController in tabBarViewControllers)
{
[viewController viewDidLoad];
}
For the time being I have also included
NSLog(#"Method called");
in the viewDidLoad of each class to test if it is being called. However the message is only being printed out when I first load the application or if I re-enter the application. This method should be called after I click the refresh button in the settings screen but it isn't and I have no idea why.
Anyone have any idea why this is not working?
From the question and your comments, it sounds like there are at least two problems:
You're having trouble accessing the view controllers managed by your app's tab bar controller.
You seem to be working against the normal operation of your view controllers.
The first part should be straightforward to sort out. If you have a pointer to an object, you can send messages to that object. If the corresponding method doesn't execute, then either the pointer doesn't point where you think it does or the object doesn't have the method that you think it does. Let's look at your code:
NSArray * tabBarViewControllers = [self.tabBarController viewControllers];
for(UIViewController * viewController in tabBarViewControllers)
{
[viewController viewDidLoad];
}
This code is supposed to call -viewDidLoad on each of the view controllers managed by some tab bar controller. Leaving aside the wisdom of doing that for a moment, we can say that this code should work as expected if self.tabBarController points to the object that you think it does. You don't say where this code exists in your app -- is it part of your app delegate, part of one of the view controllers managed by the tab bar controller in question, or somewhere else? Use the debugger to step through the code. After the first line, does tabBarViewControllers contain an array of view controllers? Is the number of view controllers correct, and are they of the expected types? If the -viewDidLoad methods for your view controllers aren't being called, it's a good bet that the answer is "no," so figure out why self.tabBarController isn't what you think.
Now, it's definitely worth pointing out (as Michael did) that you shouldn't be calling -viewDidLoad in the first place. The view controller will send that method to itself after it has created its view (either loaded it from a .xib/storyboard file or created it programmatically). If you call -viewDidLoad yourself, it'll either run before the view has been created or it'll run a second time, and neither of those is helpful.
Also, it doesn't make much sense to try to "refresh" each view controller's view preemptively. If your app is retrieving some data from a web service (or anywhere else), it should use the resulting data to update its model, i.e. the data objects that the app manages. When a view controller is selected, the tab bar controller will present its view and the view controller's -viewWillAppear method will be called just before the view is displayed. Use that method to grab the data you need from the model and update the view. Doing it this way, you know that:
the view controller's view will have already been created
the data displayed in the view will be up to date, even if one of the other view controllers modified the data
you'll never spend time updating views that the user may never look at
Similarly, if the user can make any changes to the displayed data, you should ensure that you update the model either when the changes are made or else in your view controller's -viewWillDisappear method so that the next view controller will have correct data to work with.
Instead of refreshing your view controllers when updating your tab bar ordering, why not simply refresh your views right before they will appear by implementing your subclassed UIViewController's viewWillAppear: method?
What this means is that each time your view is about to appear, you can update the view for new & updated content.

three20 - TTTableViewController Memory warning gives blank screen, how to fix?

This is driving me nuts. I am using three20's TTTableViewController and when I get a memory warning, the screen goes white. Now, after reading on the three20 google group is seems that the tableView got released. But, I cannot for the life of me figure out a check to see if that is the case, then create it again.
I was using the following because I thought it would fix the issue, but it seems that it doesn't satisfy the if statement:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// If we don't have a datasource we need to reset it
if (!self.dataSource) {
// Create datasource again
}
}//end
Does anyone know what to do when this happens? The google group has been no help.
Are you subclassing TTTableViewController? I haven't used it before, but assuming it's just like UITableViewController...
How does your "viewDidUnload" look like? Are you releasing the tableview here? If so, you need to create tableview in viewDidLoad to match it.
No need to check if dataSource is available in viewDidAppear, because if you read View programming guide, it explains that memory warning will call "viewDidUnload" to give you a chance to clean up data that are created in "viewDidLoad".
i had the same issue and it drove me crazy as well.
Nobody mentions it in the three20 docs, but you shouldn't use UIViewController's initWithNibName function to add subviews. If you do, a memory warning will release these subviews.
Try to move your code from initWithNibName function to viewDidLoad function. I have noticed that some code need to be kept in the initWithNibName, such as navigation styles. However, any subviews added to the controller's view should be in the viewDidLoad function.
In general you should be careful to set up views in viewDidLoad rather than the class constructor. For instance, you should set up your launcher view in viewDidLoad rather than the constructor of your launcher view controller, otherwise your launcher will become empty after a memory warning.
In the case of TTTableViewController however this does not (usually) apply because you don't set up the table view manually. I had the same problem you had, and eventually tracked it down: I had redefined viewWillDisappear: and forgot to call [super viewWillDisappear:animated]. This meant that some of the flags that the Three20 controller maintains about the state of the view were not updated correctly.
I also found that it was beneficial to redefine didReceiveMemoryWarning to call [self setEditing:NO] before calling super; I found that the state of the table view got confused otherwise (this is not relevant if you don't use edit mode for your table).
Finally, there is a bug in Three20 which means that tables in loading/empty/error mode will not be restored properly; see a discussion in the blog post by TwoCentStudios and a proposed fix on github.

What goes in the View and what goes in the View Controller?

I am new to programming for the iPhone and this will be my first question here. I have experience with different languages like php/java/c++.
My question is about ViewControllers and views in iOS.
I have started a project which will contain several different things like a login screen, a main screen and several other screens. The goal of this project is to learn how to create everything programmatically instead of using interface builder to get more accustomed to the system. I am using the book: "Advanced iOS 4 Programming" to help me.
I have been able to create all the screens ( and stuff like logging-in is working ), but I am not sure if I did it correctly.
All of my code for creating the textfields/labels/buttons is now located in the ViewController while the main view where everything is put on is almost empty, with nothing being done in it. Shouldn't the code to create the textfields and other components be located in the view itself, or is this the correct approach?
I have looked at several different examples but most use interface builder. The book itself is also not very clear in this matter.
Thanks in advance.
Kind regards,
Jasper
In the view you have the view - on other words, literally what the human user sees with their eyeballs.
So, for example, if you are doing complicated drawing you will have your own custom drawRect: method, and that for example is in the view.
On the other hands ......
In the view controller you have things that control the view.
Generally speaking "everything" goes in the view controller.
When you first start programming for iPhone (or Mac), simply put everything in the view controller and don't worry too much. There's plenty to learn. OK?
Eventually, separate out the "actual drawing" separately in to the view.
Hope this simple explanation for beginners helps!
In simple controller code should contain methods like...
class myLoginController : NSObject
{
UIView *myView;
}
-(void) initLoginController
-(void) loadLoginViewInView :(UIView*)inView;
-(void) removeLoginView;
-(void) isViewLoaded;
-(void) submitButtonClicked : (id) button;
-(BOOL) isLoginSuccess;
and initLoginController you can create your view,
-(void) loadLoginViewInView :(UIView*)inView
{
[inView addSubview:myView];
}
and in removeLoginView you can remove "myView" from its superView .

self.view = aViewController.view vs [aViewController loadView] -

I am trying to understand the behavior of view controllers when switching from one to another (displaying different views)
A part form the addSubiew statements which seem to work, I can't find an explanation to what happens with the two statements:
self.view = someViewController.view;
[someViewController loadView];
In fact I got a case where only the first one seems to work (the view defined within someViewController is displayed) and in another case only the second one.
More precisely, from the root viewController.view to anotherViewController.view (already istantiated) I have to use the first one, to come back I need to use the second one.
I can't understand what can it be the difference in the current situation which allows one or the other statement to work.
Thank you
It's very hard to even understand your question. But I'm gonna try to answer it:
The iPhone OS uses a stack of views and display the first one to the users.
You either pop (remove) or push (add) views to this stack.
The code you provided is somewhat "obscure". The best pratice to gain control is to add SubViews to your window or any other view
i.e. [window addSubview:viewController.view];
Don't override your current view by using self.view = someViewController.view;
Instead initialize your new ViewController and add it's view to the subview of your current view.
Any questions? Just comment.
Looks like you have some misconceptions about how view controllers work.
There are a couple things wrong with those 2 statements:
self.view = someViewController.view;
According to the docs of UIViewController.view :
"Each view controller object is the sole owner of its view. You must not associate the same view object with multiple view controller objects." -Apple Docs
Once that line of code executes, the view would have 2 different controllers, which is bad.
Next line:
[someViewController loadView];
This is bad because you should never explicitly call loadView.
From the docs of UIViewController.loadView :
"You should never call this method directly."
From the docs of UIViewController.view :
"If you access this property and its value is currently nil, the view controller automatically calls the loadView method and returns the resulting view. "
Spend some time with the viewController tutorials and guides such as "View Controller Programming Guide for iPhone OS". They are good docs and can teach a lot.