One ViewController vs. many ViewControllers - iphone

I'm building an app which consists of different views which are closely related to each other. So far, I only have one UIViewController which controls these different views. View 1 and 2 share the same background, for instance, and the transition between view 1 and 2 is a custom animation.
My problem is that both view 1 and 2 have an UIScrollView. My UIViewController is their delegate and I could have the following scrollViewDidScroll to distinguish between the two scrollviews:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.tag == 1)
//handle a
else if (scrollView.tag == 2)
//handle b
else if (scrollView.tag == 3)
//handle c
}
As a lot happens with scrollView 1 and different things happen with scrollView2, the code will become really messy. Ideally, I'd like to define in a separate file what happens if scrollView1 is scrolled etc.. Yet I don't want to have another UIViewController as then transitions become more difficult. I don't have a NavBar or ToolBar, so neither UINavigationController nor UITabBarController would work very well in my case.
What should I do?
I posted a similar question here.

If you don't want two view controllers, just create a separate delegate for each scroll view. Make it an NSObject which conforms to UIScrollViewDelegate and create it at the same time as the scroll view.
Seems to combine the results you seek: one view controller, but encapsulated scroll view code.

You could have a base controller class that handles the common functionality. Each different controller can inherit from this and override with their specific functionality as required.
Aka the template pattern
Edit
To expand. You say you want only one view controller. So you should create a separate class to handle the individual functionality. The View Controller has a base class pointer which gets swapped around according to the current view.
In pseudo code :
class BaseFunctionality
-(void) handleDidScroll {}
end
class ScrollViewAFunctionality : BaseFunctionality
-(void) handleDidScroll {
// Lots of interesting technical stuff...
}
end
class ScrollViewBFunctionality : BaseFunctionality
-(void) handleDidScroll {
// Lots of interesting technical stuff...
}
end
class TheViewController : UIViewController
BaseFunctionality *functionality;
-(void) swapViews {
// Code to swap views
[this.functionality release];
if (view == A)
this.functionality = [[ScrollViewAFunctionality alloc] init]
else if ( view == B)
this.functionality = [[ScrollViewBFunctionality alloc] init]
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[this.functionality handleDidScroll];
}
end

Related

Autorotating only some of the tabs inside a UITabBar? (ios 5)

I have a UITabBar with 5 tabs. I only wish to enable autorotation for a UIViewController that gets pushed onto the stack deep inside tab #3. So to be clear: tap UITabBar item 3, and you get tabbar item #3's root UIView, which should not autorotate. Tap and get another UIViewController pushed onto the stack (via a UINavigationController). Tap again, and get another UIViewController pushed onto the stack. Only here should this UIView autorotate.
The other 4 tabs should not rotate at all--not the root view of the tabs, nor any of the child views of the tabs.
Can someone tell me what approach I should use? I read that every single tab needs to respond "YES" to willAutorotateToInterfaceOrientation.
In each view's shouldAutorotate..., you could call a method in the root view controller that checks what is currently being displayed. If the deep-level view for tab 3 is on display, it will return YES, otherwise NO, and the views will, in turn, return the same.
Edit -- more detail per user798719's request:
Your root view controller knows which view is on display. You add a method to the root view controller - (BOOL) isDeepLevelTab3Displayed;. The method checks whether the deep-level view for tab 3 is on display and, if so, returns YES, otherwise returns NO.
Each sub view controller’s shouldAutorotate… method will get a ref to the root controller so that it can call isDeepLevelTab3Displayed.
If you’re using a navigation-style controller, you can get the ref like this (self is the sub controller):
NSArray *arrayOfControllers = [self viewControllers];
UIViewController *rootController = [arrayOfControllers objectAtIndex:0]; // per UIViewController class ref, root controller is at index 0
Or you could get anything in your project like this:
YourProjectAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
UIViewController *rootController = appDelegate.rootController; // or appDelegate.intermediateClass1.intermClass2.rootController — however you set up your project
So every sub controller would do this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
UIViewController *rootController = [[self viewControllers] objectAtIndex:0];
return [rootController isDeepLevelTab3Displayed];
}
Therefore, every subcontroller would return YES when autorotation should happen, fulfilling the requirement you mention at the end of your question.
However, if all your subcontrollers are instances of UINavigationController, you could determine which view is currently on display directly, by calling visibleViewController. Then you just need a way of checking its identity.
You could check the controller’s nibName or title, for example, against a constant, or add an integer property intControllerIdentity to all your controllers and set them in the controller’s initWithNibName…
The integer-property scheme might be best, because it won’t be affected should you later change the nibName or title.
You’d add constants to some class whose h file is imported by all the controllers (or, if all the controllers are instances of the same class, put these constants in that class’s h file):
#define kFooController 1
#define kBarController 2
#define kRotatableController 3
And you’d set it like this:
self.intControllerIdentity = kRotatableController;
And check it like this:
if (self.intControllerIdentity == kRotatableController)
Hope that helps. But evaluate this added detail with a critical eye; I have worked with autorotation but not yet with navigation controllers.

UIPickerView on Separate Page

I am working on an iPhone app. Initially, I had my pickerview in the same screen so this was just a one page app. After skinning it i realized that i want the pickerview on it's own separate page. So i did that. However, my pickerview originally would update uilabels and other objects on that same page. How can I have my pickerview access those objects from it's new view?
- (IBAction)ShowPickerAction:(id)sender {
if (self.theView == nil) {
theView = [containerView initWithNibName:#"containerView" bundle:nil];
theView.parentView = self;
}
self.theView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:self.theView animated:YES];
}
That is the method I am using to call my new view. But line 3 of the above code gives me the error "No known class name for selector initWithNibName:bundle". I think that error is related to something that i did wrong in my header file. My new class is called containerView. So i did this in my header:
#interface ViewController : UIViewController {
containerView *theView;
But that gives me the error "Unknown type name containerView" even though i do have a class named containerView!!
Look into uiappdelegate protocol or try passing values to through a static function to the previous page.
Use a delegate to pass information back and forth to the view object that instantiatrd the picker view. You want to keep your code coupling as loose as possible, especially if you might like to drop it into your next project. Using a delegate and/or blocks are some of the best ways.

Horizontal paging in my app

I want to make horizontal paging in my app.
I have big text, which placed in UITextView, but I want to make horizontal paging, like iBooks or bookmate.
Do you have any solution or idea?
Have a look at the new (iOS 5) class UIPageViewController. For the iBooks page curl effect, try using
[controller initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:nil];
You can then set the view controllers using setViewControllers:direction:animated:completion:. For more reference for this class, visit the UIPageViewController Class Reference.
Use this handy class called PagedView which manages a paging scroll view as well as view loading/unloading and reuse for you.
You have to implement two delegate methods to get it working:
- (NSUInteger)numberOfPagesInPagedView:(PagedView *)view;
- (UIView *)pagedView:(PagedView *)view viewForPageAtIndex:(NSUInteger)page;
They will look familiar if you've ever used UITableView. In numberOfPagesInPagedView: you just have to return the number of pages you want to display.
- (NSUInteger)numberOfPagesInPagedView:(PagedView *)view
{
// return the number of pages in the paging view
return 10;
}
In pagedView:viewForPageAtIndex: you have to return a view for a specific page index. You can reuse the views by sending the dequeueReusableViewWithIdentifier: message to the paged view.
- (UIView *)pagedView:(PagedView *)pagedView viewForPageAtIndex:(NSUInteger)page
{
static NSString *reuseIdentifier = #"PageIdentifier";
UIView *view = [pagedView dequeueReusableViewWithIdentifier:reuseIdentifier];
if(view == nil) {
view = [[[MyPageView alloc] initWithFrame:view.bounds] autorelease];
}
// add contents specific to this page index to the view
return view;
}
In order to get view reuse working, your UIView subclass (MyPageView) should conform to the ReusableObject protocol and implement reuseIdentifier (in this case you would return #"PageIdentifier").
I have used this class in a number of projects and it works pretty well.
UIScrollView with pageEnabled turned on?
You can use a UIWebView (subclass of UIScrollView) for your horizontal paging needs.
For it to work, you'd need to split the NSString that you use to store your text, depending on the size (and height) of your web view.
Store the split NSString into an NSArray, and then depending on user swipe, load up the correct 'page' (array index) and display with animation.
Hope that helps.

Removing a superview from a method call inside that class

I'm trying to switch between two views right now. The problem is how it is called.
Heres the easiest way to explain my situation:
I have a Parent View.
With a subclass ChildView, that contains a table.
Upon selecting an object in that table, I wish to switch to a different child view of that parent view.
Parent---------
|Child 1 |Child 2
Child 1 is a subclass of Parent to allow me to access a method in Parent that switches between Child Views 1 and 2, but for some reason it wont work when accessing it from Child 1.
Any clues on how to do this? Heres the basic code:
Child 1
- (void) changeViews
[super methodToSwitchChildViews];
Parent
- (void) methodToSwitchViews
[self.child1.view removeFromSuperView];
[self.view insertSubView:child2.view atindex:0];
Super is the class that precedes a (sub)class in inheritance. Here the children seem to be views on a superview (parent). So use superview, and not super.
Okay, I dug around quite a bit and finally figured out a solution. In case anybody ever has the same problem here's what you do:
In the .h file of the child view do
#class parentViewName
Then in the .m file add
#import "parentViewName.h"
...
- (void) functionToRemoveSelfFromView {
parentViewName *PARENT = [[parentViewName alloc] init];
// You must have a method in the parent view to toggle or remove the subview, the way
// you want it done, then call it with the new delegate. Make sure it doesn't set this
// view to nil or releases it because this method has yet to return. If animating do not
// hide this view either.
[PARENT methodToRemoveSelfFromView];
[PARENT release];
}

iPhone Programming Random Views When I tap button

I have 5 different views and when I tap the button, I want to push one of 5 views randomly. However, I do not want to make 5 different controllers for each views. Do I have a chance to put them in one controller? If so, how?
You can have as many views as you want in a single UIViewController subclass. You can create them all in Interface Builder as well, in the one .xib file for your UIViewController class (it might get a bit hard to see, though, better to lay out each UIView in its own .xib). You can present them in any combination you want. Assuming you want to just show one view at a time, you can do this:
In your viewDidLoad method, start out showing the initial view, and in your class keep track of which is the current view:
...
#property(nonatomic, retain) UIView *currentView;
...
- (void)viewDidLoad
{
[self.view addSubview:self.defaultView];
self.currentView = self.defaultView;
}
Then to switch to a particular other view do this:
- (void) switchToView:(UIView *)newView
{
[self.currentView removeFromSuperView];
[self.view addSubview:newView];
self.currentView = newView;
}
}
Or you can show them all at one time: just [self.view addSubview:theView];
You have to set the tags of all the views, and then use
int r = arc4random() % 5;
method to find the random number. Use this newly generated number to check the tag, in your selector
Just change the view outlet of the controller on the action of the button. Use random numbers to select which view. Ouh and - (void)setNeedsDisplay :)