I tried a few things myself, but couldnt really get the handle around it.
I wanna do two things:
First the user can press one of three buttons - They all link to the same ViewController, but when User Presses the first button three labels change accordingly in this second ViewController. And then the user can enter some data which will be displayed in the third view, also accordingly on which button was pressed in the first view.
I tried it with IF Statements, e.g. (IF ViewController.button1waspressed == True) and it didnt really work. I also tried it with tags e.g. (Button1.tag = 1)
Could someone give me a short example on how this could work?
FirstViewController.m
- (IBAction)switch:(id)sender;
{
SecondViewController *second =[[SecondViewController alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:second animated:YES];
SecondViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
if (sender == self.button1) {
NSString *greeting = [[NSString alloc] initWithFormat:#"Randomtext"];
self.label.text = greeting;
}
}
The problem is obvious in this one, SecondViewController cant see the property from the first one. (And yes I imported the FirstViewController and vice versa)
Your buttons should all directly call IBActions (methods defined like so):
- (IBAction)doSomething:(id)sender;
Defining them as IBActions exposes them to be connected with the blue connection lines in interface builder. Once you've hooked them up and the method is being called, you can simply use an equality check on the sender parameter, which the calling button will automatically set as itself.
if (sender == self.myButton) {
// do something
}
Here I'm assuming that you've got a property called myButton in your ViewController, which would be an IBOutlet:
#property (nonatomic, retain) IBoutlet UIButton *myButton;
This exposes that property to be connected with the blue connection lines in interface builder, so your ViewController will know exactly which button you're talking about when you say myButton.
Related
I will start off by describing what I'm trying to accomplish and then follow by describing what I've tried already. I am pretty sure what I've tried is NOT the best approach so please correct my approach as needed!
I have a ViewController.m and a Custom View which is laid out in CustomView.xib. The custom view has UIButtons and UILabels which are populated from an Array of Custom Objects.
The user flow should go as such: ViewController starts off showing CustomView with its labels populated by
CustomObjectArray[0] -> User presses button -> Another "copy" of CustomView slides into the view, over the previous version.
It's labels and buttons are populated by CustomObjectArray[1] -> User presses button -> repeat until end of Array.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So far I made a ViewController.m/h, a CustomView.m/h AND a CustomView.xib file. I used interface builder to do the layout.
On the "Custom Class" tab for the top-level View in XIB file, I type in "CustomView". I also drag IBOutlets from the XIB file to the CustomView HEADER (.h) file.
In the ViewController, under -(instancetype) init method, I create a custom view using the normal initWithNib method. And then I do:
self.view = CustomViewVariableName;
When I run the program, the view show's up fine. However, when I try to selector's, nothing's getting recognized by the buttons:
[currentCustomView.continueButton addTarget:self action:#selector(continueButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
What did I do wrong here?
More importantly, given my described goals up top, am I even doing this right? Do I need to have CustomView.m/h files? Or can I do the same thing with ONLY the XIB and the ViewController file. Remember that I need to have "multiple copies" and slide them on top of each other until the end of my custom objects array.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some more code as requested.
In ViewController:
- (instancetype)init
{
currentCustomView = [[[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil] objectAtIndex:0];
self.view = currentQuizQuestionView;
.......
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[currentCustomView.continueButton addTarget:self action:#selector(continuePressed:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)continuePressed:(id)sender{
NSLog(#"Current position");
//[self moveInQuestion];
}
In CustomView.h:
#import <UIKit/UIKit.h>
#interface CustomView : UIView
#property (weak, nonatomic) IBOutlet UIButton *continueButton;
#end
In CustomView.m:
this is just the default page, I added nothing in this file.
CustomView.xib:
I'm not sure if I fully understand your requirements. Let's assume you would like to have several CustomViews as subviews of your ViewController and those subviews can be display each by each after pressing its own button.
First thing about adding subview:
self.view = CustomViewVariableName;
Since CustomsViews will be subviews according to my assumption, above line is an error. You probably would like to have something like this.
for (NSInteger i = 0; i < 5; i++) {
CustomView *v = [[CustomView alloc] initWithNib];
[v.button addTarget:self
action:#selector(continuePressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:v];
}
Second, I noticed you would like to have an array as a handler to refer all the subviews. We can add above CustomView into a mutable array by insert one more line into above for-loop.
[_customObjectArray addObject:v.button];
Finally, I created a simple project and tried to implement things you mentioned. Maybe you can take it for a reference. https://db.tt/Och2tzyG
I have a UITableView that i would like to hide until the user taps the button searchButtonTapped. (I'm also using this button as an IBAction.)
Originally i'm hiding the table view as you see in the viewDidLoad, and i wanna show it after the button was tapped, but it does not shown up after i tap the search button. Do i missed something? For me, it seems it should be work properly, after the button was tapped i refresh the table view.
my .h file
#property (weak, nonatomic) IBOutlet UIButton *searchButtonTapped;
- (IBAction)searchButton:(id)sender;
.m file
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.hidden = YES;
}
- (void)buttonTapped:(id)sender {
if (sender == self.searchButtonTapped) {
self.tableView.hidden = NO;
[self.tableView reloadData];
}
}
- (IBAction)searchButton:(id)sender {
[self searchSetup];
}
It's impossible to tell from the little bit of code that you posted. Add NSLog statements in your buttonTapped method that show entering the method, entering the if statement, the value of searchButtonTapped, and the value of self.tableView.
Then you can tell if the method is getting called, if the if statement is evaluating as true, and if the table view is non-nil. One of those things is likely to be the cause of your problem.
I'm guessing that the if statement is wrong. what type is the property self.searchButtonTapped? Post the code that declares that property.
Based on the name I would guess that searchButtonTapped is a boolean?
you have declared only one IBAction, which is for the method searchButton.
This method call the searchSetup´s method. What is the purpose of it?
- (IBAction)searchButton:(id)sender {
[self searchSetup];
}
So you must have another IBAction for buttonTapped method witch is currently a "void" method and not a IBAction. Or you make that connection from the storyBoard, or you must declare it programaticly like:
[self.searchButtonTapped addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside]
I'm new to Objective-C and have a question. Did the search multiple times but I couldn't find what I was looking for.
I'm using storyboard for this app. On the homescreen you've got some buttons with labels above them. Those labels should tell a number. When pushing the button you go to a new viewController where you have input that (after 'save') goes back to the homescreen and updates the label with the correct number. All that works great for one button and I'm very happy about it.
The problems are:
1. Since I have multiple buttons with labels, I want to use the same viewController to give input over and over again. I tried connecting every button to slide to the viewController under the identifier "AddData", but Xcode doesn't allow the same identifiers twice or more in storyboard. So I would need something else for this. Any idea?
2. Currently I use the following code to bring back the data to the homescreen:
homeScreenViewController
- (IBAction)unwindToHomeScreen:(UIStoryboardSegue *)segue;
{
inputDataViewController *source = [segue sourceViewController];
self.logoOneLabel.text = source.endTotalNumber;
}
inputDataViewController:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.saveButton) {
return;
} else {
if (endTotalLabelNumber > 0) {
self.endTotalNumber = [NSString stringWithFormat:#"%.0f", totalLabelNumber + endTotalLabelNumber];
} else if (endTotalLabelNumber == 0 && totalLabelNumber == 0){
self.endTotalNumber = 0;
} else {
self.endTotalNumber = [NSString stringWithFormat:#"%.0f", totalLabelNumber + endTotalLabelNumber];
}
}
}
This works great for the one button, but how to use this with multiple? I heard about Delegates to use the same viewController multiple time and get data back to different places, but I just don't get it. Any help?
You shouldn't need delegates.
What you will need is a property on the view controller that handles input to it knows which button it is handling input for.
When you segue to the input controller, set this property, based on which button was pushed. When you unwind back, fetch this property to know which label to modify.
For example, in your input view controller's .h file, add a property like this:
#property (nonatomic,assign) NSInteger handlingTag;
Or something, whatever name makes sense to you.
Now you need to implement your home screen view controller's prepareForSegue:sender:.
Use the sender argument to determine which button was pushed, and based on that, set the input view controller's new handlingTag property based on the button in a way that you will know what to do with it when we unwind.
Now in the unwind method:
switch (source.handlingTag)
Create a switch structure based on the source's handlingTag property, and set the appropriate label based on this value.
As Jeff points out in the comments, it'd be a really good idea to define an NS_ENUM to use here for the property rather than an NSInteger. The NS_ENUM would allow you to name the values you're using.
There is a few different way to implement what you need. But i think most common its a delegate.
This is how your inputDataViewController looks like:
#import <UIKit/UIKit.h>
#protocol inputDataDelegate;
#interface inputDataViewController : UIViewController
#property (weak) id<inputDataDelegate> delegate;
#property (strong, nonatomic) NSNumber *buttonTag;
#end
#protocol inputDataDelegate <NSObject>
-(void) inputDataViewControllerDismissed:(id)data;
#end
Then in #implementation, you should in "save" button action, message to you delegate method :
[self inputDataViewControllerDismissed:#{#"buttonTag":buttonTag,#"endTotalNumber":endTotalNumber}
Next in homeScreenViewController connect delegate :
#interface homeScreenViewController : UIViewController<inputDataDelegate>
After that in #implementation:
-(void)inputDataViewControllerDismissed:(id)data
{
// if you use modal
[self dismissViewControllerAnimated:YES completion:nil];
// or if you use push
//[self.navigationController popViewControllerAnimated:YES];
switch (data[#"buttonTag"]) {
case 1:
self.lableWtiTagOne = data[#"endTotalNumber"];
break;
case 2:
self.lableWtiTagTwo = data[#"endTotalNumber"];
break;
// number of cases depend how many buttons you have
}
Also, most important, thing didn't forget send self to our delegate:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"inputDataController"])
{
inputDataViewController *inputCtrl = [segue destinationViewController];
inputCtrl.delegate = self;
inputCtrl.buttonTag = sender.tag
}
}
I am currently designing the structure for my first iPhone game and ran into a problem. Currently, I have a 'MenuViewController' that allows you to pick the level to play and a 'LevelViewController' where the level is played.
A UIButton on the 'MenuViewController' triggers a modal segue to the 'LevelViewController'.
A UIButton on the 'LevelViewController' triggers the following method to return to the 'MenuViewController':
-(IBAction)back:(id)sender //complete
{
[self dismissModalViewControllerAnimated:YES];
}
The problem is, I have a UILabel on the menu page that prints the number of total points a player has. Whenever I go back to the menu from the level, I want this label to automatically update. Currently, the label is defined programmatically in the 'MenuViewController':
-(void)viewDidLoad {
[super viewDidLoad];
CGRect pointsFrame = CGRectMake(100,45,120,20);
UILabel *pointsLabel = [[UILabel alloc] initWithFrame:pointsFrame];
[pointsLabel setText:[NSString stringWithFormat:#"Points: %i", self.playerPoints]];
[self.pointsLabel setTag:-100]; //pointsLabel tag is -100 for id purposes
}
self.playerPoints is an integer property of MenuViewController
Is there a way I could update the label? Thanks ahead of time!
This is a perfect case for delegation. When the LevelViewController is done, it needs to fire off a delegate method which is handled in the MenuViewController. This delegate method should dismiss the modal VC and then do whatever else you need it to do. The presenting VC should normally handled the dismissal of modal views it presents.
Here is a basic example of how to implement this:
LevelViewController.h (Above the Interface declaration):
#protocol LevelViewControllerDelegate
-(void)finishedDoingMyThing:(NSString *)labelString;
#end
Same file inside ivar section:
__unsafe_unretained id <LevelViewControllerDelegate> _delegate;
Same File below ivar section:
#property (nonatomic, assign) id <LevelViewControllerDelegate> delegate;
In LevelViewController.m file:
#synthesize delegate = _delegate;
Now in the MenuViewController.h, #import "LevelViewController.h" and declare yourself as a delegate for the LevelViewControllerDelegate:
#interface MenuViewController : UIViewController <LevelViewControllerDelegate>
Now inside MenuViewController.m implement the delegate method:
-(void)finishedDoingMyThing:(NSString *)labelString {
[self dismissModalViewControllerAnimated:YES];
self.pointsLabel.text = labelString;
}
And then make sure to set yourself as the delegate for the LevelViewController before presenting the modal VC:
lvc.delegate = self; // Or whatever you have called your instance of LevelViewController
Lastly, when you are done with what you need to do inside the LevelViewController just call this:
[_delegate finishedDoingMyThing:#"MyStringToPassBack"];
If this doesn't make sense, holler and I can try to help you understand.
Make a property self.pointsLabel that points to the UILabel, then you can just call something like [self.pointsLabel setText:[NSString stringWithFormat:#"Points: %i", self.playerPoints]]; to update the label with the new score
In your modal view header file, add the property:
#property (nonatomic,assign) BOOL updated;
Then in your main view controller, use didViewAppear with something like:
-(void)viewDidAppear:(BOOL)animated{
if (modalView.updated == YES) {
// Do stuff
modalView.updated = NO;
}
}
Where "modalView" is the name of that UIViewController that you probably alloc/init there.
Add more properties if you want to pass more info, like what level the user picked.
I have a main view with 3 buttons. Clicking on any of the buttons adds a SubView.
The buttons have different titles and are all linked to IBAction "switchView"
The "switchView" code is below.
- (IBAction)switchView:(id)sender{
secondView *myViewController = [[secondView alloc] initWithNibName:#"secondView" bundle:nil];
[self.view addSubview:myViewController.view];
}
The "secondView" loads up correctly and everything works well.
The problem is I want to be able to know which button was the Sender.
I don't want to create 3 subviews, one for each button. The code and XIB would be absolutely the same>
The only difference would be a variable that I would like to set up in the second view (viewDidLoad method) depending on who is the Sender (which button was clicked)
Is this possible? Or I would need to create 3 subViews - one for each button?
Your help is greatly appreciated!
You can identify different buttons with the tag property.
e.g. with your method:
-(IBAction)switchView:(id)sender {
UIButton *button = (UIButton*)sender;
if (button.tag == 1) {
//TODO: Code here...
} else if (button.tag == 2) {
//TODO: Code here...
} else {
//TODO: Code here...
}
}
The tag property can be set via the InterfaceBuilder.
Hope this helps.
I think you can solve in 2 ways:
Create a property like:
#property (nonatomic, strong) IBOutlet UIButton *button1, *button2, *button3;
in your viewcontroller and link the buttons to them as referencing outlet on the XIB.
Give a different tag to each button on your xib and ask for the tag of the sender with UIButton *b=(UIButton*)sender; b.tag; like Markus posted in detail.
Solving my problem it all came down to transferring data between the mainView and subView.
In my mainView.h I declared an NSString and its #property
...
NSString *btnPressed;
}
#property(nonatomic, retain) NSString *btnPressed;
...
then in my mainView.m inside the switchView method I did this:
- (IBAction)switchView:(id)sender{
secondView *myViewController = [[secondView alloc] initWithNibName:#"secondView" bundle:nil];
btnPressed = [NSString stringWithFormat:#"%i", [sender tag]];
[myViewController setBtnPressed:self.btnPressed];
[self.view addSubview:myViewController.view];
}
This line in the code above actually takes care of transferring the data to the newly created subView:
[myViewController setBtnPressed:self.btnPressed];
Then in my secondView.h I declare exactly the same NSString *btnPressed and its #property (though this a completely different object than the one declared in main)
Then in my secondView.m I get the value of the button pressed I'm interested in.
- (void)viewDidLoad {
[super viewDidLoad];
int theValueOfTheButtonPressed = [self.btnPressed intValue];
}
This works well.
Don't forget to #synthesize btnPressed; as well as [btnPressed release]; in both mainView.m and secondView.m