I've an object of UIImageCustom that extend UIImageView, and in a method this class I want to get instance the ViewController, where my object UICustom is added.
In touchesBegan method of my class UIImageCustom, when I touch in UIImage, I want add other UIImage in my ViewController:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIImageView* newImageView=[[UIImageView alloc] initWithFrame:CGRectMake(60, 200, 200, 200)];
//Here I need add newImageView in my ViewController
[??? addSubview:newImageView];
}
Someone can help me?
You'll have to add a custom property to your UIImageCustom class that contains a reference to the owner view controller if you want to do what you're saying you want to do. But there's a better way.
What I suggest you should be doing is implementing touchesBegan in the view controller that owns the view, and performing any custom logic there.
There are a bunch of ways you could do this (set a custom property in your UIImageCustom class, just have UIImageCustom add this new image view to its own superview (e.g. [self.superview addSubview:newImageView]). But these approaches seem conceptually flawed. A view shouldn't be adding anything other than to itself (and if you turned off clipping, adding this new image view as a subview of the UIImageCustom, itself, might be another approach, certainly less offensive than the previous two I alluded to).
But I might suggest a couple of other approaches:
If your goal is simply to avoid duplication of your touches code, I might put it in a gesture recognizer subclass, rather than attaching it to some random UIImageView subclass. Gesture recognizers is the right place to put this sort of touch-related code. See the Gesture Recognizers section of the Event Handling Guide for iOS.
Or if you really need to marry this gesture with with a bunch of image views, I might use a custom container view controller to mediate this interaction between gestures and image views. See Creating Custom Container View Controllers in the View Controller Programming Guide for more information.
But you could wrap the touches code and the handling of these two image views all within a view controller, and then use view controller containment to add that to an existing view controller.
We don't know enough about the particular design to suggest one over another, but those are a couple of ideas to pursue. Most likely, the custom gesture recognizer approach should be more than sufficient.
Create a category like this
#interface UIViewController (TOP_MODAL)
-(UIViewController*) topModalController;
#end
#implementation UIViewController (TOP_MODAL)
-(UIViewController*) topModalController{
if (self.presentedViewController == nil){
return self;
}
return self.presentedViewController.topModalController;
}
#end
then
AppDelegate* delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
UIViewController* viewController = [delegate.window.rootViewController topModalController];
...
[viewController.view addSubview:newImageView];
Related
How can a subview access the methods from its superview? I have a button, and when pressed I would like the button to call a method from its superview, but I do not know how.
suppose your super View class name is
MainView.h
sub View Name
SubView.h
So In sub class you can do
MainView *myMainView = (Mainview *)[self superview];
[myMainView someMethod];
Make sure someMethod is public Method.
Other way you could have reference to all the view is set a tag
For example
myMainView.tag = 100; or self.tag = 100;
In the subview you could do
MainView *myMainView = (Mainview *)[self viewWithTag:100];
[myMainView someMethod];
a weird construct but just call the method:
inside a view you have have self.superview
since self.superview is a UIView*, the compiler will claim it is invalid to call method XYZ on it. Cast it to id or to your class name to use it
e.g.
[(id)self.superview myMethod];
or even
id myValue = [(id)self.superview myMethod:param1];
One method is to use delegates.
#protocol ButtonHandlingDelegate <NSObject>
- (void) subviewButtonWasPressed;
#end
In your Subview add this:
#property (nonatomic, weak) id selectionDelegate;
When subview is created, set delegate to superview.
Define Superview as delegate in .h file
#interface SuperView : UIView <ButtonHandlingDelegate>
in Superview .m file
- (void) subviewButtonWasPressed{
// Do somethign about it
}
All of the answers listed are hacky and bad style. You should be using delegation through the subview's ViewController. What you will want to do is create a protocol for your subview with a void method called something like specialButtonPressedOnView:(SUBVIEWCLASS *)view. Then in the subview's view controller you should make yourself the delegate for this protocol and respond to the delegate method from the VC's context.
Using self.superview is a bad idea because you cannot guarantee what your superview is going to be and generally blindly calling method on objects (ESPECIALLY those cast to id) is a really bad idea.
The other answer that suggested having the superview implement the subview's protocol is also not good style because you're creating a dependency between your views. If you were thinking of going down this path your subview should probably just be a subclass of the superview.
One of the core parts of the MVC design pattern is making sure that your views are reusable and totally independent. They are just structures that you can inject your content into, to which they will respond with pretty UI, and they don't need to know anything about what's being passed to them and don't have to ask other views for help. So either using delegation through the subview's ViewController or subclassing is probably the best direction. Both methods preserve the MVC pattern and are much safer than the other suggestions.
I have a UIViewController say viewControllerA which contains some view element like UIButton, UILabel etc. Now my question is should I create those view elements in a separate UIView class and then add in UIViewController, or should I create those view elements directly inside the UIViewController. Accordingly to MVC isn't it appropriate to create view elements inside a separate UIView class and then add this in UIViewController?
The standard place to build the view hierarchy in a UIViewController is in the -viewDidLoad method. That method gets called whenever the UIViewController's view is created. The view controller's view will be loaded from the NIB/Storyboard if applicable; your outlets will be wired up; and then -viewDidLoad is called for you to perform further customization:
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel *aLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0,0.0,100.0,40.0)];
[self.view addSubView:aLabel];
}
In Cocoa/Cocoa Touch you don't always want to subclass everything the way you would in, say, Java. There are often other preferred means of extending the functionality in built-in classes such as Objective-C categories, delegation, and pre-defined properties.
It's certainly possible to do this sort of thing another way, but this is the most "Cocoa-like" way to do it. Actually, the most "Cocoa-like" way would be to create the view hierarchy in Interface Builder, but if you want to do it programmatically this is the usual way.
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!
I have a view with several lines drawn in it in different directions. I need to determine which line was tapped by the user and then respond accordingly.
I have a few different ideas in my head, but I want the best, most efficient way to do this...
Ultimately what makes the most sense to me is having each line in a separate view and treated like individual objects. If I did this would I need to position and rotate the view to be exactly where the line is so I know when it is tapped? If not I would assume the views will overlap each other and I would not be able to determine what line was tapped.
I hope I am making sense. Please let me know the best way to achieve this. Thanks!
For me the best way to approach this is create UIView's as lines. If they are simply lines with plain colors just use the background view and set the CGRectFrame accordingly.
In order to react to touch event's without dealing with positions etc, create a touchEvent in the init method of the UIView like this:
UITapGestureRecognizer *onTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(lineClicked)];
[self addGestureRecognizer:onTap];
Declare the function inside the UIView class:
-(void)lineClicked {
//You can check some #property here to know what line was clicked for example
if (self.color == [UIColor blackColor])
//do something
else
//do another thing
// You can use a custom protocol to tell the ViewController that a click happened
(**) if ([self.delegate respondsToSelector:#selector(lineWasClicked:)]) {
[self.delegate lineWasClicked:self];
}
}
(**) You will probably want to put some logic into your viewController after clicking in the line. The best way to approach this is to declare a #protocol in your CustomUIView.h file and pass self as parameter so the viewController knows who was clicked:
#protocol LineClikedDelegate <NSObject>
#optional
- (void)lineWasClicked:(UIView *)line; //fired when clicking in the line
#end
Finally, create a #property in your CustomUIView to point the delegate:
#property id<DisclosureDelegate> delegate;
And in the ViewController. When you create lines as UIViews set the delegate like:
blackLine.delegate = self.
Implement the method - (void)lineWasClicked:(UIView *)line; in the ViewController and you are set.
As requested I'm adding this as an answer:
You could use layers instead of views for displaying the lines. This is both efficient in drawing and allows for hit testing to determine which line has been tapped.
Here's code for the geometry calculations.
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.