If I want to extend a UIButton for example and have it present a new view. Can I create a method within my custom UIButton class to do this. If I initialize the button with a target method that ends up switching views it needs to know what view it is currently in.
ExpandedViewController *expandView = [self.storyboard instantiateViewControllerWithIdentifier:#"ExpandedView"];
[self presentViewController:expandView animated:NO completion:nil];
This is what the code would look like in a view controller, but how can I pass the view to the button so that instead of self, it knows what view to target.
The short answer is that yes, this is possible.
The long answer is that you shouldn't do it because it breaks the rules of MVC. Your button is a view and should not be acting as a controller (this is what the controllers are for). Instead, leave the logic to your view controllers.
To get the correct view controller set as the target of your button, you can do this:
In viewDidDisappear: of your view controllers, remove the action for the controller that is going away from the button:
[myButton removeTarget:self
action:#selector(myButtonWasPressed:)
forControlEvents:UIControlEventTouchUpInside];
And then in viewWillAppear: of your view controllers, add the action for the controller that is being presented:
[myButton addTarget:self
action:#selector(myButtonWasPressed:)
forControlEvents:UIControlEventTouchUpInside];
Give your button a containingViewController property and don't forget to set it as you move the button around.
Related
I have a UIViewController (HomeView) that shows my UIView called GameView via a Segue.
[self performSegueWithIdentifier: #"segue_playgame" sender: self];
The GameView calls a UIView (PauseView) when the use presses a button. This pause view is shown via just adding the PauseView to the UIView.
UIView *pv = [[PauseView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:pv];
To remove the PauseView I call
[pv removeFromSuperview];
Is there a way to call an "End Game" method in the PauseView that will remove both the PauseView and the GameView taking the user back the HomeView (UIViewController)?
And side note, is there a better way to handle showing views and removing them? Or is how I am doing it pretty much standard?
What you are missing here is a UINavigationController. From the official documentation:
The UINavigationController class implements a specialized view
controller that manages the navigation of hierarchical content. This
navigation interface makes it possible to present your data
efficiently and makes it easier for the user to navigate that content.
You generally use this class as-is but you may also subclass to
customize the class behavior.
With your views managed by the UINavigationController stack you can use:
- (NSArray<__kindofUIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated
To pop back to the root view controller which in your case is your home view controller.
I have created a button in a UIView subclass . I need to call popViewControllerAnimated via this button , but nothing work ! and I cannot see the viewController push back to rootViewController . here is my code :
- (void)SomeFunction {
backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton showsTouchWhenHighlighted];
[backButton addTarget:self
action:#selector(backToMainMenu)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)backToMainMenu {
[self.window.rootViewController.navigationController popViewControllerAnimated:YES];
NSLog(#"back");
}
I change the code to this :
UINavigationController *vc = self.window.rootViewController.navigationController;
[vc.navigationController popViewControllerAnimated:YES];
but nothing happens .
I think you need to use the proper target format which takes the button as an argument. So add target function like this:
[backButton addTarget:self
action:#selector(backToMainMenu:)
forControlEvents:UIControlEventTouchUpInside];
and the target should look like this:
- (void) backToMainMenu:(UIButton *) sender{
[self.navigationController popViewControllerAnimated:YES];
}
A better option is to use Delegate pattern because in your current logic you are breaking the MVC architecture and guidelines.
Create a Protocol in your subview class. The receiver of this delegate would be the view controller class from which you are showing your view. In event handling of the button, call the delegate method and from the view controller you would be able to call popViewControllerAnimated successfully.
I believe your fundamental problem (besides design) is in (void)backToMainMenu ... self.window.rootViewController.navigationController will be nil, so this method does nothing
from UIViewController class reference:
If the receiver or one of its ancestors is a child of a navigation controller, this property contains the owning navigation controller. This property is nil if the view controller is not embedded inside a navigation controller.
so you see, the rootViewController cannot be embedded inside a nav controller can it, it is the bottommost one..
why don't you test this:
{
UINavigationController *vc = self.window.rootViewController.navigationController;
if (vc==nil){
NSLog(#"nav controller is nil, this will never work");
}
[vc.navigationController popViewControllerAnimated:YES];
}
I also fully agree with #rory's answer there about design..
PS did you actually create a UINavigationController in order to push this viewController?
I know how to change between tabs when I am currently on one of the views presented by the tab controllers, using this:
self.tabBarController.selectedIndex=1;
But right now I need to change between tabs while on a modal child view, I need the modal view to be dismissed and the other tab to be shown,
The best/proper pattern for this is to have the modal child hand off the task to its delegate as it exits. Define your own simple "myChildViewDelegate" protocol (could be a single method, even), and give the modal child a "delegate" property like so:
id<myChildViewDelegate> delegate;
When the user presses a button or whatever on the modal view, it calls a method on its delegate and the delegate dismisses the modal view and changes tabs.
Assuming you have some sort of button on the presented view controller just set that buttons target to a function in the presenting view.
-(void) someFunction {
... Code that creates the modal view controller and buttons
[modalViewController.changeTabButton addTarget: self action:#selector(changeTab:) forControlEvents:UIControlEventTouchUpInside];
[self presentViewController:modalViewController animated:YES completion:nil];
}
-(void) changeTab:(id)sender {
self.tabBarController.selectedIndex=1;
}
I have a UIView that has some buttons that I need to add actions to.
I've tried the this:
HeaderViewController * header = [[HeaderViewController alloc]initWithNibName:#"HeaderViewController" bundle:[NSBundle mainBundle]];
[header.aboutUs addTarget:self action:#selector(aboutUsPage:) forControlEvents:UIControlEventTouchUpInside];
[header.MyLibrary addTarget:self action:#selector(myLibraryPage:) forControlEvents:UIControlEventTouchUpInside];
[self.tableView addParallaxWithView:header.view andHeight:229];
But the button is not responding at all.
What have I done wrong.
Here's the thing, You are creating the view controller and then setting targets and actions for buttons that are referenced as the view controller's property.
I suspect that if you log or put a breakpoint just before you set the target and actions and then step through, the buttons will be nil.
View controllers load their views lazily. So although you have created the view controller, you haven't asked it to load it's view. So the buttons have not been created from the nib. So the properties are nil, and not target action is getting set.
The first option to fixing it is to move the last line so it looks like this:
[self.tableView addParallaxWithView:header.view andHeight:229];
[header.aboutUs addTarget:self action:#selector(aboutUsPage:) forControlEvents:UIControlEventTouchUpInside];
[header.MyLibrary addTarget:self action:#selector(myLibraryPage:) forControlEvents:UIControlEventTouchUpInside];
That way you are calling the view, which will initialise it, and the button parameters should not be nil after this.
The classier way of doing this sort of thing is to define a protocol for responding to clicks on the button, and make the calling view controller a delegate that implements the protocol methods. That way you only have to set the delegate when you create the object, and you don't have to configure the buttons each time.
If you write :
[header.aboutUs setTarget:self];
AND if your class (I mean here self) implements the function aboutUsPage:, it should work.
my iOS application use storyboard
it have 2 view controllers:
- main storyboard view controller
- and popover view controller with some objects in it
i've got a button on main view controller and it creates programing every time i run the application:
*CGRect buttonFrame = CGRectMake(10., 10., 120., 50.);
oneButton = [UIButton buttonWithType:UIButtonTypeCustom];
[oneButton setImage:[UIImage imageNamed:[NSString stringWithFormat:#"someImage.png", img]] forState:UIControlStateNormal];
[oneButton setTag:img];
[oneButton setFrame:buttonFrame];
[oneButton addTarget:self action:#selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:oneButton];*
the action of this button show my popover view like that:
*- (void) pressButton:(id)sender {
popoverViewController *popoverFrame = [self.storyboard instantiateViewControllerWithIdentifier:#"myPopoverView"];
popoverWithObjects = [[UIPopoverController alloc] initWithContentViewController:popoverFrame];
[popoverWithObjects presentPopoverFromRect:[sender frame] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}*
from now the situation is, that i can't send to my button any message or result.
i want to say to my program button (note - i've got only sender of this button action:#selector(pressButton:) ) that popover return some result or some action of an object in popover send anything (string for example)
Or in another words when i interact with any object like button on popover view, i want to change parent buttons title label
Use NSNotificationCenter.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsnotificationcenter_Class/Reference/Reference.html
You need to use delegate. Check out my answer to this similar question from this SO.
Edit: Link to tutorial on Storyboard and delegate pattern usage. And my original answer to delegate on this SO
How about writing a function on your Main View Controller to do what you want. Then call that function from the popover? (i.e. use 'prepare for segue' to send the popover the id of the Main View Controller, and then use that id to call the function on Main View from the popover)