hope someone can help me on this as been stuck for hours.
I am trying to make a kind of picture book.
I have a view which is my container and I add subviews to that by using addsubview.
On the subview, I have swipe gestures etc that I want to trigger off method in the parent view. I worked out how to trigger the delegate but I cant get the delegate to trigger the parent view. I have read over 10 different ways of doing it and none work.
I now very confused about what a super view is to. Just to confuse matters, the delegate has a tabcontroller and the parent view is tab button 1
I tried
[self.view.superview method]
[self.superview method]
On the delegate I tried
self.tabcontroller.parentviewcontroller, selectedview, super view.super
UPDATE :
The subview needs to be independant of the parent view as its a reusable view.
Also I have not set the parentview to superview as I just thought a superview is a view with subviews (please don't kill me). So maybe I just need to set the parentview to a superview?
The proper way of doing such things is to use protocol and delegate pattern.
Define a protocol like
#protocol subViewDelegate
-(void)somethingHappened:(id)sender;
#end
then implement that protocol in your superview :
#interface superView:UIViewController<subViewDelegate> {
...
}
...
#end
define a delegate property in your SubView like this
#interface subView : UIView {
id<subViewDelegate> delegate;
...
}
#propery (nonatomic, assign) id<subViewDelegate> delegate;
...
#end
the in your subview, call the delegate like this
[self.delegate somethingHappened :self];
It's a little hard to help you without any code given, but let's try:
Create a protocol: Name it however you like (I will call it "MyProtocol") and add to it the definition of the function you want to call in your superview, let's call it "respondToSwipe"
If your superview is a UIView, you have to create your own subclass of UIView and make your superview an instance of that class.
Let your (newly) created superview class implement the protocol of 1.) an implement the "respondToSwipe" method
Create an instance variable of the the type id in your subview, and name it however you like, e.g. "myDelegate".
Pass the superview created in 2/3.) to your "myDelegate" variable
Call [myDelegate respondToSwipe] whenever you like
For a custom view, you could subclass UIControl and use control events:
Define some control events. You're free to make up 4 control events (UIControlEventApplicationReserved = 0x0F000000)
Have whoever wants to receive events call addTarget:action:forControlEvents:
Have the control call [self sendActionsForControlEvents:events]
Alternatively, you could use a UIGestureRecognizer-style interface (addTarget:action:).
Alternatively just use UIGestureRecognizer (OS 3.2+)
Did your parent view set itself to be the superview of the subview when it added the subview? Otherwise the subview doesn't know who its superview is.
The more standard way of naming things to call the method handler the delegate instead of the superview, make it a property, and have the subview check for both the existence of the delegate being set and whether it can handle the method.
Here a very good example of how apply the delegation pattern on the iPhone. I downloaded the code an it works pretty good.
http://www.hivestudio.cat/index.php?option=com_content&view=article&id=57:technical-note-the-delegation-pattern-on-the-iphone&catid=35:technical-note-category&Itemid=76
Related
I have a subclass of UIViewController that I want to add from the storyboard.
So I'm using what seems the standard methodology:
SubViewController *svc = [self.storyboard instantiateViewControllerWithIdentifier:#"SubViewControllerID"];
[self addChildViewController:svc];
[self.view addSubview:svc.view];
Which is fine but what if I want to call a custom init method on the subview?
I can do something like:
svc = [svc initWithFoo:#"Hello"];
Which seems to have to go after the addSubview call inorder for it to work.
Is this the best way to do this?
Seems a bit unorthodox. Calling an init method on an object that has already been created seems like its no longer truly an init method.
Maybe I should call it setWithFoo: or something and not have it return anything?
SubViewController *svc = [self.storyboard instantiateViewControllerWithIdentifier:#"SubViewControllerID"];
will cause the SubViewController to be inited with it's - (id)initWithCoder:(NSCoder *)decoder {} method.
Override that method (don't forget to call super)
If you want to do additional setup to your view controller after you instantiate it form the storyboard you can create some methods in the view controller's class and call them after the instantiate method fo the storyboard.
But be careful, if you try to make changes on any UI component in those methods, they wont be applied, and probably the app will crash. So use those methods to set params to the View Controller like array of objects, or any kind of data, and apply the UI changes for the view controller's view in viewDidLoad/viewWillAppear/viewDidAppear methods of your view controller.
Essentially I think the answer is that you can't use custom initialisers on ViewControllers added from the storyboard. Instead you have to set properties directly or through a method at the appropriate time in the life cycle as stated above.
Also as mentioned, the VC will be instantiated through initWithCoder, so calling an additional initialiser might be superfluous(?).
I encountered problems trying to use a custom initialiser that contains a call to super if I called it before the subview was added. I would just get a blank view added, I think because the superclass doesn't seem to know about the storyboard at that point. I had more success removing the call to super but that seems wrong.
This case would be more pertinent when adding subviews to a scrollview. For simplicity I left this out of my example.
I have an iPhone app primarily made of two views, let's call them fullScreen and cardViews (the cardViews are presented as subViews of the full screen views). I've handled all of the animations in presenting the card Views by having a masterCardViewClass and a masterFullScreenViewClass. All specific cardViews are subclasses of the masterCardView, all specific fullScreenViews are subclasses of the masterFullScreenView.
I present a cardView with a method from the masterFullScreenViewClass. I dismiss the cardView with a delegate method from the masterCardView. However, I'm having a problem calling a method to present a cardView from another cardView. All cardView presenting methods are contained in the masterFullScreenViewController class.
How do I access these methods without copying a pasting them locally where I need them.
One way of doing it is by using protocols.
In a nutshell, your masterCardView class would implement a protocol method that presents a cardView (for the sake of simplicity, let's say that you invoke cardViews with a certain index):
masterCardView.h:
#protocol CardPresenterDelegate <NSObject>
- (void)presentCardViewWithIndex:(int)index;
#end
#interface MasterCardView:UIViewController <CardPresenterDelegate>
...
masterCardView.m:
- (void)presentCardViewWithIndex:(int)index
{
// Code for presenting a cardView
}
You would also need to create a delegate (weak) property in your cardView:
cardView.h
#property (weak) id<CardPresenterDelegate> cardPresenterDelegate;
And then by accessing that property in your cardView, you can tell the masterCardView to do something for you:
cardView.m
[self.cardPresenterDelegate presentCardViewWithIndex:5];
Oh, and, don't forget to set the delegate property on your cardViews when creating them in your masterCardView:
back in masterCardView.m:
cardView.cardPresenterDelegate = self;
In my iPhone project I have a UIViewController in which I add an instance of a subclass of UIView. From this instance I need to communicate back an integer to my view controller. My current solution is to send a message from my instance to my App Delegate which sends a message to the view controller. It works, but it feels a bit messy.
Is there anyway I can send a message straight back to the view controller?
I would love to be able to do something like [super doSomething:123];
Any ideas?
Thanks
This is the kind of thing that NSNotificationCenter was provided for. Once you get handy with sending and receiving notifications, your message-passing gets a WHOLE lot simpler.
One of the classic things people confront is how to get a pointer to the object they want, in order to tell it about something. How do I, for instance, tell the ViewController two slots back up the UINavigationController stack that the user just changed this data field? So you dig into the stack, offset back by some magic number of elements in the stack, build public setters on the fields you want talk to... It's super cumbersome.
Compared to registering as a notification receiver in one place, and then firing a notification in some complete other place when the data changes. It's kind of magical, after doing all the "dig through the view hierarchy" work.
Um, I'm not sure I understand your problem correctly. You have a class derived from UIView which needs to send a message to another class derived from a UIViewController. It sounds like you are creating the UIView instance programmatically. Is there any reason my you could not have a property on the UIView which refers to the UIVIewController and just use that to send it a message directly.
You cannot use [super ...] because the super of your UIView derived class would be UIView.
Or am I miss-understanding the issue :-)
If I understand correctly, you want to send a message from your subclass of UIView to the view controller.
That means your subclass of UIView needs to have a property or ivar which is the view controller. The easiest way to do this is to add it as an outlet and connect it to the view controller in the nib file.
Generally you should not go via the app delegate. Having a typed pointer link is also less than ideal.
The optimal way of communicating - Apple does it like this as well - is to create a delegate protocol. When creating the view controller you pass a pointer to the delegate as id . Then when it gets to sending the message you ask the delegate:
if ([delegate respondsToSelector(didFinishSomething:)])
{
[delegate didFinishSomething:info_to_pass];
}
If you want to be extra-sophisticated then you can also add a pointer to the calling class instance. Like:
[delegate myViewController:self didFinishSomething:info_to_pass];
This way you always know what kind of class the message is coming from.
If there is more than one place that needs to be notified of a change, then instead of delegation you will use notifications.
In my iPhone project I have a
UIViewController in which I add an
instance of a subclass of UIView.
This implies that you have both a reference to the instance of the UIView subclass and the UIViewController in the same scope. I.e. something equivalent to:
UIViewControllerSubclass *myViewController;
UIViewSubclass *myView;
(It doesn't matter if they are actually instance variables or, even, globals)
And once those two variables are initialized, somewhere you do something like:
myViewController.view = myView;
In your UIViewSubclass, add a property that points back to your UIViewControllerSubclass:
#property(assign) UIViewControllerSubclass *myController;
Then, when you do the above assignment, add:
myView.myController = myViewController;
From there, messaging your controller from your view is easy:
[self.myController yoManHereIsAnInt: 42];
Note that I used assign instead of retain because the controller already retains the view. If the view were to also retain the controller, you would have a cycle that would eventually lead to a leak.
No super about it. super is entirely related to the inheritance hierarchy of your Objective-C classes. What you are asking has nothing to do with inheritance and everything to do with how the various instances of objects in your application are connected together.
Simply add an outlet to your UIView subclass, connect it to its view controller in Interface Builder, and call your method on that. Here’s how that might look:
MyUIView.h:
#interface MyUIView : UIView
{
UIViewController *viewController;
}
#property (assign) IBOutlet UIViewController *viewController;
#end
MyUIView.m:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[[self viewController] setTitle:#"Hello from MyUIView"];
}
I am using a UINavigationController to switch between views. What I would like is for each view to have the ability to control when it is swapped out for another view by having buttons within the view. All of the samples I've seen thus far have placed buttons on a toolbar, which is located on the root view containing the Switch View Controller rather than the views, them self. Is it possible to do what I want? I can't figure how to wire up the connection back to the UINavigationController.
I'm having a difficult time wording this, so please feel free to let me know if you need additional clarification.
Read about delegates. Delegates are a common method to signal stuff from objects to their "parents" or any other objects.
You should have a "delegate" property (can really be called anything, this is just a convention) on your child views. You can have buttons in your child views.
You declare the delegate like this:
interface ChildView : UIViewController {
id delegate;
}
#property (assign) id delegate;
implementation ChildView
#synthesize delegate;
Then, when you set up your child views inside your UINavigationController, you do:
ChildView *childView = [[ChildView alloc] init...]
childView.delegate = self;
Inside your child view, you have a button method:
- (IBAction) didPressButton:(id)sender {
[self.delegate didPressButtonToSwapView];
}
Inside your UINavigationController, you have a method:
- (void) didPressButtonToSwapView {
[self popViewController]; // use the right names, I made these up :)
[self pushAnotherViewController];
}
You should also read about protocols which would make the above code more robust and would help you make sure you only call the right methods on delegate, but I did not want to complicate this example.
EDIT: yes, the cleanest way to get rid of the warning is to use a protocol. Just put this in a separate .h file:
#protocol SwitchingDelegate
- (void) didPressButtonToSwapView;
#end
Include this .h in the UINavController header, and say the UINavController implements the protocol:
#interface MyNav: UINavController <SwitchingDelegate> { ...
Implement the method in the implementation (you don't need anything more in the interface).
In your ChildView, say that the delegate must implement the protocol: change all the declarations to:
id<SwitchingDelegate> delegate;
The compiler then helps you by checking whether the delegate objects really implement the protocol. You should not get any warnings when you have completed all of this correctly.
I've created a view controller and the associated view using the interface builder. I'm trying to call a function that I added to the UIView from the UIViewController. I'm not sure how to call that function though.
I've tried
[self.view myFunction]
but that just makes my program crash.
Did you declare your IB outlets and connect the method to it?
Your view (more accurately nib)'s file owner should be set to your viewController class.
Unless your calling drawing functions and methods you shouldn't call anything on the view. You just don't need to.
Edit: grammar corrections.
How is the view getting initialized? Is it a custom view type? If your view is being initialized from a nib, make sure that the class on the view type is the class that has the method implemented.
It is probably crashing because UIView does not have a method named myFunction. In order to add myFunction to a UIView object, you need to subclass it.
I assume you have subclassed your UIView, and called it something like MyView. You also probably have subclassed your UIViewController and called it MyUIViewController. Therefore you may have:
#interface MyViewController : UIViewController {
MyView *myView;
....
}
Now you can call [self.myView myFunction];
That's one way. Another way may be:
[(MyView *)self.view myFunction];
This depends on whether myView is the first view in the hierarchy (or should be set up in your NIB files).