So I have this little project in which the user goes from one view to another as they click buttons, and the text on the last button they've clicked should show up in a label in the top of the screen. I figured out how to have the variables accessible from anywhere in the app, and also how to change it as the user presses the buttons. Yet, I can only make the label's text change once a button is hit, and I wanted it to happen automatically (the view to appear with the label already containing the correct text).
For that I set the label in two different ways (which I found online):
#interface ViewMateria : UIViewController
{
IBOutlet UILabel *materiaLabel;
}
#end
and
#interface ViewMateria : UIViewController
#property (weak, nonatomic) IBOutput UILabel *materiaLabel;
#end
Still, for both of this I can not access the label by a simple materiaLabel.text or anything like that, and can't find a way to do it without the need for the press of a button.
I've been looking for an answer for this for a while now, and nothing that I found seemed to be of use for me (since most of the links I found taught how to change a label text with the press of a button).
This is my first project with Objective-C and Xcode, and I don't really know the answer, so maybe I am missing something quite obvious, but still, if anyone could point me in the right direction, that would be of much help.
Put this in your .h file for the view controller:
#property (weak, nonatomic) IBOutput UILabel *materiaLabel;
When you are about to present the view controller, access it like this:
your_ViewMateria_object.materiaLabel.text = #"";
Then present it..
Are you using segues?
If yes, its another story..
UPDATE:
For segues use this, with the .h property value I just mentioned:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
ViewMateria *vm = [segue destinationViewController];
vm.materiaLabel.text = #"Whatever";
}
You should be good to go now ;D
Did it work?
UPDATE
In second view controller .h file
#property (strong, nonatomic) NSString *buttonName;
.m file
self.materiaLabel.text = self.buttonName;
In first view controller .m file
inside IBAction
secondviewcontrollerObject.buttonName = [(UIButton *)sender currentTitle];
Related
I have a randomly generated int that I assign to a tag for a UIImageView on my storyboard in ViewWillAppear.
When I segue to the main menu and try to enter the view again, however, the app crashes. I know it's because of the tag because when I remove it everything works fine.
Any thoughts on why it works the first time but not on times after that? Code below.
ViewController.h:
#interface ViewController : UIViewController{
int tagnumber;
IBOutlet UIImageView *box;
...
}
ViewController.m:
-(void)viewWillAppear:(BOOL)animated{
tagnumber = arc4random()%1000;
box.tag = tagnumber;
...
}
- (IBAction)unwindToThisViewController:(UIStoryboardSegue *)unwindSegue
{
[_animator removeAllBehaviors];
[box removeFromSuperview];
}
MainMenu.m:
-(IBAction)prepareForUnwind:(UIStoryboardSegue *)segue {
}
Basically, when the view disappears and the system is running out of memory, it will call on UIViewController's implementation of didReceiveMemoryWarning. There is a fantastic description of this here: ViewWillDisappear versus dealloc
When you are creating an IBOutput you should really have a weak pointer to it by saying #property (weak, nonatomic) IBOutlet UIImageView *box. Views retain their subviews, and therefore, if a view gets deallocated, its subviews retain count decreases by one. When the view disappears, the system will decide when to deallocate self.view, and when it does, it will automatically release box, and box will get deallocated as well. You don't really need to worry about it.
Newbie question for Xcode gurus...
I have two views. They both use the same custom class. In view_1 I have a button and when this is pressed view_2 will show. In view_2 I have a label which will have it´s text changed when I press the button in view_1. As of now the Label_1 is nil when I set a breakpoint at it and therefor useless. How can I get to update this label when I press the button? Her are some snippets from my code...
This is my .h file:
#interface ViewController : UIViewController
{
IBOutlet UIButton *buttonSelectTimeInterval;
IBOutlet UILabel *labelTimer;
}
#end
This is the button action in my .m file:
- (IBAction)startPouring_ButtonClick:(id)sender
{
labelTimer.text = #"foo";
}
…but my .m file doesn't seem to know the labelTimer since it is a ´nil´. Why is this so? It is instantiated in the .h file.
Anyone?
You can use NSNotificationCenter. Put this in you IBAction.
[[NSNotificationCenter defaultCenter] postNotificationName:#"buttonPressed" object:nil];
And this to your viewDidLoad.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selectorhere) name:#"buttonPressed" object:nil];
somewhere in your .m
(void)selectorhere {
labelTimer.text = #"foo";
}
You can use NSNotificationCenter for this.
Here are the apple documentation link.
First, nothing is "instantiated" in the .h file - that's just the public listing of what properties and methods are available to other classes. Think of the header file as a table of contents, but only for the things the class wants others to see.
Those properties don't exist in memory until the instance of the class itself is created, and then only if you set them to some initial value once they're needed.
How & where are you creating the 2nd view? Is it a storyboard segue or something? The 1st view doesn't seem to have any way of knowing the 2nd one exists, so it won't be able to see or access the label.
View1Class.m
#import View2Class.h
#implementation View1Class
- (IBAction)startPouring_ButtonClick:(id)sender {
//Instantiate the 2ndView when you need it.
// This gives View1 a reference to View2 and its public UILabel.
View2Class * my2ndView = [[View2Class alloc] init];
my2ndView.labelTimer.text = #"foo";
}
#end
As I said, it's still not clear how/where you're actually displaying the 2nd view though, so the snippet above is incomplete. You could use a modal w/a delegate, or this is where NSNotificationCenter is a helpful option - the 2nd view can sign up to get notifications & change accordingly. There are numerous tutorials about creating a 2nd/modal view and displaying it on a button click - you should probably look at those to clarify how the structure of such an app ought to work.
This answer should get you on the right track.
Other specific issues:
Why is the label nil? Because there isn't one...
In this IBAction, which seems to be in View 1:
- (IBAction)startPouring_ButtonClick:(id)sender
{
labelTimer.text = #"foo"; //this is looking for labelTimer in the clicked view.
}
... it is looking for its own labelTimer IBOutlet (in which case it should probably be self.labelTimer.text), and not that of the 2nd view. If the 1st view doesn't even have a UILabel IBOutlet, this is another problem.
If the views have different functions & different properties, they probably shouldn't be instances of the same custom class. If the 1st view doesn't have or need a UILabel, it shouldn't have one in its .h. If the 2nd view doesn't have or need a button it shouldn't have one in its .h. If the views serve different purposes, then make them different classes.
BTW,
Since you're using instance variables for your IBOutlets, you'd need to write your own getter & setter methods if you want to change their values. Did you? To make those values accessible to other classes, you'd need to make those methods public & put them in the .h. It's not good practice for an instance to set its instance variables directly w/o a getter/setter, and other objects definitely should not.
The preferred method is to use #properties for your IBOutlets instead of declaring them as instance variables. This will automatically create the getter & setter methods, backing store in memory, and as of XCode 4.4 it automatically adds #synthesize so you no longer need to do so. Declaring your IBOutlets as "weak" references prevents retain cycles & memory leak, where the view holds on to the outlets & the outlets hold on to the view & nothing ever goes away...
View1Class.h
#interface ViewController : UIViewController
#property (nonatomic, weak) IBOutlet UIButton *buttonSelectTimeInterval;
#end
View2Class.h
#interface ViewController : UIViewController
#property (nonatomic, weak) IBOutlet UILabel *labelTimer;
#end
I have custom UIButton which programmatically interacts (triggers) with ViewController methods via protocol. But the behaviour of the button has to be dependent on the ViewController placed on. I do this to minimise amount of code in ViewControllers itself, as the button has to remain the same and bear the same functions (navigation).
Is there any way in UIButton's custom class to get the ViewController it is placed on?
I'd follow #rmaddy advice in a specific way, borrowing from the SDK's style
// MyCutomButton.h
#protocol MyCustomButtonDatasource;
#interface MyCustomButton : UIButton
#property(weak,nonatomic) IBOutlet id<MyCustomButtonDatasource>datasource;
// etc
#end
#protocol MyCustomButtonDatasource <NSObject>
#optional
- (NSString *)howShouldIBehave:(MyCustomButton *)button;
#end
Now the button can have it's datasource set in IB. View controllers that include it will need a little additional code (sorry, it's unavoidable in a good design). They will declare themselves as implementing MyCustomButtonDatasource.
When MyCustomButton needs to behave conditionally based on where it's placed, it can ask its datasource...
// MyCustomButton.m
NSString *string = #"defaultBehavior"; // per #RichardTopchiy's suggestion
if ([self.datasource respondsToSelector:#selector(howShouldIBehave:)])
string = [self.datasource howShouldIBehave:self];
// string is just made-up here, have it answer something simple (int, BOOL)
// that lets the button proceed with the right behavior. Don't ask for
// anything that relies on specific knowledge of how MyCustomButton
// is implemented
EDIT - To create the relationship, if you've decorated the property as an IBOutlet (as shown above), you should be able to setup the relationship in IB. Declare your view controller as implementing <MyCustomButtonDatasource>. Select your custom button, then the connections inspector, then drag to your view controller.
Alternatively, make the button itself an IBOutlet property in the view controller and, in viewDidLoad, do:
self.customButton.datasource = self;
The last way to do it is give your button a tag, say, 128, then:
MyCustomButton *customButton = (MyCustomButton *)[self.view viewWithTag:128];
self.customButton.datasource = self;
I am just start studying iOS developing watching Stanford iOS course, but it looks like I have already missing something.
I have a form with UILabel and UIButton. When an user press the button the title of the button must be added to the text of label.
Here is my current CalculatorViewController.h:
#import <UIKit/UIKit.h>
#interface CalculatorViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *display;
#end
and here is a CalculatorViewController.m:
#import "CalculatorViewController.h"
#implementation CalculatorViewController
#synthesize display = _display;
- (IBAction)digitPressed:(UIButton *)sender {
NSString *digit = [sender currentTitle];
UILabel *myDisplay = self.display;
myDisplay.text = [myDisplay.text stringByAppendingString:digit];
}
#end
The problem is that self.display (and myDisplay) variables have a nil value. Looks like I need to do something to link my variable with control on the form. What ?
Thanks in advance.
You need to link the control, the UILabel in Interface Builder to the variable in your CalculatorViewController class.
It is very likely that the file's owner (talking about the Xib file) is your CalculatorViewController, so you need to Control+drag the file's owner (or the object representing your VC) to the control and you will be shown a menu with the possible IBOutlet variables declared in your class, so you select the one you want to represent the control.
You can check if this link is properly set in two ways: (1) Select the UILabel in Interface Builder and see if there's a connection to the variable in the Connections Inspector, or (2) In your code you'll see a bullet near the IBOutlet declaration, if the bullet is hollow the connections is not set, if the bullet is filled the connection is set.
There is no need of this line
UILabel *myDisplay = self.display;
You have already declared your label in your interface file
- (IBAction)digitPressed:(UIButton *)sender
{
NSMutableString *string = [myDisplay.text stringByAppendingString:sender.titleLabel.text];
myDisplay.text = string;
}
I am new to IOS, Xcode and MVC. I am on a steep learning curve and am failing with what I assume is a most basic task.
I have a tabbed application with two tabs. Both tab views communicate with a web service and I want to add an image to each tab view, changing the image to indicate the connection state.
So, I created a third .xib file with a controller class (IconViewController). I am hoping to add and remove an instance of this icon view in each of the tab views.
Here is the pseudo code for my icon view:
#interface IconViewController : UIViewController
{
UIImageView *_icon;
}
#property (nonatomic) IBOutlet UIImageView *icon;
- (void)setForBusy;
- (void)setForOk;
- (void)setForFail;
And implementation:
#implementation IconViewController
#synthesize icon = _icon;
-(void)setForBusy
{
// Set Busy Icon Image
}
-(void)setForOk
{
// Set Ok Icon Image
}
-(void)setForFail
{
// Set Fail Icon Image
}
The icon IBOutlet is connected to an UIImageView on the accompanying xib file.
Here is one of the root tab controllers:
#import "IconViewController.h"
#interface TaboneViewController : UIViewController
{
IconViewController *_iconViewController;
}
#property (nonatomic) IBOutlet IconViewController *iconViewController;
and implementation:
#synthesize iconViewController = _iconViewController;
- (void)viewDidLoad
{
[super viewDidLoad];
self.iconViewController = [[IconViewController alloc]
initWithNibName:#"iconViewController"
bundle:nil];
[self.view addSubview:self.iconViewController.view];
}
In the tabView xib Interface Builder I added an Object and made it a class type IconViewController. I connected the Icon View Controller Object->Reference Outlet to the File Owner->iconViewController Outlet.
Running the project I get the error:
loaded the "iconViewController" nib but the view outlet was not set.
I have experimented with other connections but with no luck. It seems to me that my first connection should work but it doesn't.
Any idea what I am misunderstanding? Is the principle good (loading an instance of third view into two root views)? If so, what outlet needs connecting?
Many thanks, Polly
I see your issue. You want to have common stage of image for both tab. I think it is better to implement subclass of UIView (or UIImageView) and implement all methods like set (void)setForBusy and etc. The stage of image you should receive from parent ViewController, something like UINavigationView controller (if you have it). Otherwise you should save stage somewhere else. My personal opinion it is too expensive to create new controller just for your purpose.
Hope it helps.