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 :)
Related
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
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.
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.
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];
}
I want to change an image on a view, from a popup dialog of 4-6 icons (imagine like changing your image on a messenger application).
The way I implement this modal popup is by creating a new view at IB, with opacity on the background, and then I load this as a subview:
IconsViewController *iconsViewController = [[IconsViewController alloc] initWithNibName:#"IconsView" bundle:nil];
[self.view addSubview:iconsViewController.view];
So, when the user touches an icon, I have
- (IBAction)iconIsSelected:(id)sender {
switch ([sender tag]) {
case 1:
[(ParentViewController*)[self superview] changeIcon];
break;
case 2:
// same here..
break;
default:
break;
}
[self.view removeFromSuperview];
[self release];
}
The changeIcon just sets the image to a corresponding icon.
As you can guess, this is not working - the changeIcon message never works.
I can't understand what am I doing wrong, any help much appreciated!
You have a few choices here...
First one is create a property on your IconsViewController of type ParentViewController*, for example:
#property (readwrite,nonatomic,assign) ParentViewController* parentController; // weak reference
To break this down further:
readwrite because we want to be able to access the value via [self parentController] but also change it via [iconsViewController setParentController:self]
nonatomic because I'm not too worried about threading
assign to make it a "weak reference" where the parent will not be retained by the child. If they each retain the other, it could lead to memory leaks later because unless explicitly released you'd end up with a retain circle causing neither object to hit a zero retain count.
When you load from nib, set the property:
IconsViewController *iconsViewController = [[IconsViewController alloc] initWithNibName:#"IconsView" bundle:nil];
iconsViewController.parentController = self;
Then, call to it from inside of iconIsSelected like this:
[[self parentController] changeIcon];
Alternatively, you can create a delegate protocol:
#protocol IconViewSelectedDelegate (NSObject)
- (void) changeIcon;
#end
And use that protocol as a property, instead of the parent view controller type. This is more abstract, but it keeps the design cleaner. The parent view controller would then implement that delegate protocol, as one of many others.
Another option is to use NSNotificationCenter and publish/subscribe to events from your dynamic view. This is the "loosest" coupling between the two objects, but it might be overkill for this scenario.
The superview of a view is a view, not a view controller, yet you cast the superview to be of class ParentViewController. If the view has no superview, it returns nil, and message to nil are no-ops (which explains why you don't crash there).
BTW, that [self release] at the end is highly suspicious.