Calling method from another class doesn't work properly - ios

I am trying to change the text and the position of a UILabel from another class.
I have successfully managed to call my -changeLabel method, which is in my FirstViewController class, from my SecondViewController class. Here is the code I have used in my two classes:
SecondViewController:
#import "FirstViewController.h"
#interface SecondViewController ()
#property (nonatomic,strong) FirstViewController *firstViewController;
#end
#implementation SecondViewController
#synthesize firstViewController;
- (IBAction)button:(id)sender {
firstViewController = [[FirstViewController alloc] init];
[firstViewController changeLabel];
}
FirstViewController:
- (void)changeLabel {
NSLog(#"changeLabel is called!");
label.text = #"New text.";
label.frame = CGRectMake(10, 100, 150, 40);
NSLog(#"%#", label.text);
NSLog(#"%f", label.frame.size.width);
}
The weird thing is that the logger looks like this after pressing the "button" that calls the method:
"2013-12-30 19:24:50.303 MyApp[655:70b] changeLabel is called!"
"2013-12-30 19:24:50.305 MyApp[655:70b] New text."
"2013-12-30 19:24:50.308 MyApp[655:70b] 0.000000"
So it seems the label text change, but it doesn't show up on the screen. And the label width is logged as 0.0.. even though I just set it to 150.
Why is this happening? Am I not able to change frame variables from another class? Is there another way to do this?
IMPORTANT:
As the FirstViewController is the main view controller while the SecondViewController is a side menu, similar to the facebook app:
I want to be able to press a "button" on the SecondViewController(side menu) and call a method in the FirstViewController(main) that changes the position(frame) of a UILabel.
EDIT:
Here is how I created the UILabel:
label = [[UILabel alloc] init];
label.frame = CGRectMake(0, 0, 150, 40);
label.text = #"Text."
[self.view addSubview:label];

I think problem is this. You are calling method from new instance of FirstViewController.
Let assume
1. FirstViewController at stack[0].
2. SecondViewController at stack[1].
If you are navigating or moving from
FirstViewController->SecondViewController
In this case FirstViewController already in memory with some address 0x23ffff.
And in SecondViewController you are again creating new instance of FirstViewController which is point to another address '0x234jurhu`
- (IBAction)button:(id)sender {
firstViewController = [[FirstViewController alloc] init];
[firstViewController changeLabel];
}
Don't create new instance here.
You can use delegate or NSNotification concept for this.

How are you displaying FirstViewController?
Here is the issue:
firstViewController = [[FirstViewController alloc] init];
[firstViewController changeLabel];
You creating a new instance of FirstViewController and updating the label text. If your using these VC's in a navigation stack and you pop back to FirstViewController from SecondViewController, you won't see any label change because they are different instances of the class.
If your using FirstViewController as a childViewController of SecondViewController (with naming of them I don't think this what your doing), then in the - (IBAction)button:(id)sender method you don't need to instantiate a new instance of FirstViewController on each button press.

I have figured out a way to do this thanks to "#Gaurav Wadhwani" answer on this question: call method from other class (self issue).
I added this code in my FirstViewController.h:
+ (FirstViewController *)singletonInstance;
And then added this code in my FirstViewController.m
static FirstViewController *_singletonInstance = nil;
+(FirstViewController*)singletonInstance
{
#synchronized([FirstViewController class])
{
if (!_singletonInstance)
_singletonInstance = [[self alloc] init];
return _singletonInstance;
}
return nil;
}
+(id)alloc
{
#synchronized([FirstViewController class])
{
NSAssert(_singletonInstance == nil, #"Attempted to allocate a second instance of a singleton.");
_singletonInstance = [super alloc];
return _singletonInstance;
}
return nil;
}
Then I added this code in my SecondViewController to run the changeLabel method:
[[FirstViewController singletonInstance] changeLabel];
And that seems to work just fine so far. I hope it wont cause any other "problems" in the future, but right now it seems to be perfect.

Try doing this:
[label setNeedsDisplay];
After the last line in changeLabel.

Related

Override Custom UITableView Class Method

I am using MWPhotoBrowser to display some photos in an app and I would like for example to change the title of the navigation controller. With a value I have in my current view controller. From the current view controller I modally call MWPhotoBrowser like this
MWPhotoBrowser *browser = [[MWPhotoBrowser alloc] initWithDelegate:self];
//Tried this below didn't work
browser.title = #"MY NEW TITLE";
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:browser];
nc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:nc animated:YES completion:nil];
So I went into the actually files of this class and in MWPhotoBrowser.m there is a function like this
- (void)updateNavigation {
NSUInteger numberOfPhotos = [self numberOfPhotos];
self.title = [NSString stringWithFormat:#"%lu Photots", photosText];
}
And I could just change it manually (hard code a value) there but I want the value to change depending on my original view controller, so I want to pass it a value to be able to set it on my original view controller. So I tried something like this, below updateNavigation I put
-(void)updateTitle:(NSString*)title {
self.title = title;
}
And then in my original view controller I tried
MWPhotoBrowser *browser = [[MWPhotoBrowser alloc] initWithDelegate:self];
[browser updateTitle:#"TEST TITLE"];
But this didn't work either, so how can I set the title of the MWPhotoBrowser form my current view controller, without hard coding a value?
Thanks
updateNavigation can be called anywhere after you called your updateTitle: method, so thats why it didn't work. Also, changing/adding something to external libraries is a bad idea, because when you udpate them you have to remember to apply all your changes again.
Now the easiest way to achieve what you want would be to create a simple subclass of MWPhotoBrowser, something like this :
CustomTitlePhotoBrowser.h
#import <MWPhotoBrowser/MWPhotoBrowser.h> //I'm not sure if this is a correct import, change accordingly
#interface CustomTitlePhotoBrowser : MWPhotoBrowser
-(void)updateTitle:(NSString *)title;
#end
CustomTitlePhotoBrowser.m
#import "CustomBrowser.h"
#interface CustomTitlePhotoBrowser()
#property(nonatomic, copy) NSString *customTitle;
#end
#implementation CustomTitlePhotoBrowser
-(void)updateTitle:(NSString *)title {
self.customTitle = title;
[self updateNavigation];
}
-(void)updateNavigation {
self.title = self.customTitle;
}
#end
And to use it :
CustomTitlePhotoBrowser *browser = [[CustomTitlePhotoBrowser alloc] initWithDelegate:self];
[browser updateTitle:#"TEST TITLE"];
There are two things added in the subclass :
We add a stored property customTitle, so that whenever updateNavigation is called, we still remember what we want to show. I also decided to call [self updateNavigation] here - this may not be needed, but gives you a possibility to change the title while the browser is shown.
We override updateNavigation - this is the clue of all of this. When using CustomTitlePhotoBrowser, whenever the original code calls updateNavigation, our overriden implementation will be called instead.

Change UILabel Text from UiViewController in another UIViewController

Okay it seems like I have made some mistakes and I did not get it with pointer and initializations by now...
Here is the problem :
I have a UIViewController for a registration process called : RegisterViewController
It calls a method in its ViewDidLoad :
[self performSelector:#selector(activateUsernamePopover) withObject:nil afterDelay:0.1];
This method looks like this :
- (void) activateUsernamePopover {
PopoverViewController *popcontroller = [[PopoverViewController alloc] init];
popcontroller.title = nil;
[popcontroller setPopoverText:#"Test"];
FPPopoverController *popover = [[FPPopoverController alloc] initWithViewController:popcontroller];
popover.arrowDirection = FPPopoverArrowDirectionUp;
popover.border = NO;
popover.tint = MgoGreyTint;
[popover setShadowsHidden:true];
[popover presentPopoverFromView:_usernameInput]; }
This will made a Popover visible. This works great.
But I Do have a few more TextFields where I want to show a Popover with a different text.
So I made a method in the PopoverViewController called setPopoverText :
- (void)setPopoverText:(NSString *)text {
[_popoverLabel setText:text];
[_popoverLabel setNeedsDisplay]; }
I call it in my activateUsernamePopover method :
[popcontroller setPopoverText:#"Test"];
And there is the problem.
I can log the text in the PopoverViewControllers method setPopoverText its fine.
But it did not change the text. I logged the _popoverLabel like this :
NSLog(#"%#",_popoverLabel);
and its (null).
I know there is some issue with the pointer or the instance of PopoverViewController I am working with, but objective c is not that clear to me yet.
Anyone got some answers for me ?
How can I change the Text of that UILabel ?
I also could imagine giving the Text to the Controller while instancing it.
Something like that :
PopoverViewController *popcontroller = [[PopoverViewController alloc] initWithPopoverText:#"Test"];
But I don´t know how. I don´t need to change the Text while the popover is visible. It will be released when the user taps in the TextField or elsewhere.
Thanks so far.
Since the UILabel is not created yet when you call init method. the way to do it is to keep text in the NSString property.
In you PopoverViewController, create the init method like this
#interface ViewController : UIViewController
- (id)initWithPopoverText:(NSString *)text;
#end
In the implementation file, keep hold of the text in the property and on viewDidLoad, you could set the text to the label.
#interface PopoverViewController ()
#property (nonatomic) NSString *popoverText;
#end
#implement PopoverViewController
- (id)initWithPopoverText:(NSString *)text {
self = [super init];
if (self) {
_popoverText = text;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
//set label.text here
self.popoverLabel.text = self.popoverText;
}
#end

Can't see view controller content after pushing to table view controller

I'm new i iOS and Objective-C world, I want to create an example app to learn something but i find some problems in my program.
I Create a TableViewController with Books names, and after touch a book I want see a some more inforamtion about this book.
So I created book class what is a view controller, i created a some Labels inside this class with some text.
NSLogs works fine. After touch the record, apps pushing to new view controller from table view controller worsk good, but I can't see any content over there instead of white bg and back button at the top.
this is book class:
#import "BooksViewController.h"
#interface BooksViewController ()
#end
#implementation BooksViewController
- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if(self){
self.title = self.bookName;
self.view.backgroundColor = [UIColor whiteColor];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
UILabel *bookNameLabel = [[UILabel alloc] init];
bookNameLabel.text = self.bookName;
bookNameLabel.frame = CGRectMake(10, 10, 300, 50);
[self.view addSubview:bookNameLabel];
UILabel *authorNameLabel = [[UILabel alloc] init];
authorNameLabel.text = self.authorName;
authorNameLabel.frame = CGRectMake(50, 50, 300, 40);
[self.view addSubview:authorNameLabel];
UILabel *bookDescLabel = [[UILabel alloc] init];
bookDescLabel.text = self.bookDesc;
bookDescLabel.frame = CGRectMake(50, 50, 300, 40);
[self.view addSubview:bookDescLabel];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
this is a methods in table view controller witch I use to create a books inforamtion window:
- (void)viewDidLoad {
[super viewDidLoad];
self.bookNames = #[#"Pan Tadeusz", #"Potop", #"Lalka", #"Uczta dla wron", #"Symfonnia C++"];
self.authorsName = #[#"Adam Mickiewicz", #"Henryk Sienkiewicz", #"Bolesław Prus", #"George R.R Martin", #"Jerzy Greborz"];
self.bookDescs = #[#"Opis Pan Tadeusz", #"Opis Potop", #"Opis Lalka", #"Opis Uczta dla wron", #"Opis Symfonnia C++"];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Książka na pozycji %ld tapped",indexPath.row);
BooksViewController *bookVC = [[BooksViewController alloc] init];
bookVC.bookName = self.bookNames[indexPath.row];
bookVC.authorName = self.authorsName[indexPath.row];
bookVC.bookDesc = self.bookDescs[indexPath.row];
NSLog(#"Nazwa wybranej książki: %#",bookVC.bookName);
[self.navigationController pushViewController:bookVC animated:YES];
}
You're doing this wrong.
First, create an iVar that will save the Indexpath that the user selected, for that, simply add an NSIndexPath variable at the very top of your .m file.
#implementation yourControllerNameHere (){ //In your code you will have your controller name, just add the NSIndexpath ;)
NSIndexPath *selectedPath;
}
You then need to perform a segue when you select a tableview cell, for that, replace your -didSelectRowAtIndexPath method with this :
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Książka na pozycji %ld tapped",indexPath.row);
selectedPath = indexPath; //We're saving the selected path to our instance variable ! This is very important otherwise we can't find it again.
[self performSegueWithIdentifier:#"fromBooksToDetail"];
}
and add the -prepareForSegue method in your .m file ; it should be there when you first created it ! just find it and add the following
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"fromBooksToDetail"]){
BooksViewController *bookVC =(BooksViewController*)segue.destinationViewController;
bookVC.bookName = self.bookNames[selectedPath.row];
bookVC.authorName = self.authorsName[selectedPath.row];
bookVC.bookDesc = self.bookDescs[selectedPath.row];
NSLog(#"Nazwa wybranej książki: %#",bookVC.bookName);
}
}
Now this will NOT work unless you add a segue link between your two viewcontrollers in storyboard, so simply open your storyboard and, using a right clic or a ctrl+left clic, drag your mouse from your TableViewController to your BooksController. Don't forget to give it the right identifier in the Attributes Inspector on the right panel !
Note that I wouldn't have named them like that ; BooksViewController has more sense if it's the TableViewController name, and "BookDetailViewController" for the detail page. But that's just a detail.
Once you have the segue link, the performSegue call and the prepareForSegue method, you'll be all set ;)
You need to learn how to debug. Try putting NSLog(#"bookname: %#", self.bookName) in your viewDidLoad and see if you print anything. (Most probably not)
I'm not 100% sure how the view life cycle works when you use alloc init for a view controller. But my bet is that the properties are not set there. Try moving bookNameLabel.text = self.bookName; to viewWillAppear instead of viewDidLoad
You should use storyboards instead. It's super easy.
Also, your initWithNibName will never get called. You are using the init-method.

how to create a subclass UIViewController by passing class name to a method?

I have 4 subclass UIViewController, named AViewController,BViewController,CViewController,DViewController.
now in a toolbar, click A, content view show AViewController's view, ...etc.
I am a kind of lazy man, hate writing 4 times "alloc]initwithnibname" codes, so I wrote following code to create them in code.
- (void)addChildView:(UIViewController *)childViewController className:(NSString *)viewClassName{
if (childViewController != nil) {
// add view
[self.contentView addSubview:childViewController.view];
}else
{
// init.
Class v = NSClassFromString(viewClassName);
UIViewController *childViewControllerNew = nil;
childViewControllerNew = [[v alloc] initWithNibName:viewClassName bundle:nil];
[self.contentView addSubview:childViewController.view];
}}
But this won't create any ViewController, always return nil when debugging.
Could you tell me what is the problem? Can I created subclass UIViewController by this approach?
Thanks in advance!
The problem is that you're creating a view controller, but then you aren't doing anything with it. There are 2 issues:
A typo means you're setting the wrong view ([self.contentView addSubview:childViewController.view];)
You aren't retaining childViewControllerNew
You should change the last block of code to something like:
Class v = NSClassFromString(viewClassName);
UIViewController *childViewControllerNew = nil;
childViewControllerNew = [[v alloc] initWithNibName:viewClassName bundle:nil];
[self.contentView addSubview:childViewControllerNew.view];
[self addChildViewController:childViewControllerNew];
if your are really lazy you can declare enum values like
typedef NS_ENUM (NSInteger, VC) {
VCa,
VCb,
VCc,
VCd
};
and then in your do
- (void)addChildView:(UIViewController *)childViewController className:(VC)typeOfVC {
switch(typeOfVC)
case VCa: ViewControllerA *vc = [ViewControllerA alloc] initwithNibName#"YourNibName"]
[self.contentView addSubview:vc.view];
case: VCb : same with B
etc
etc
}
also, you can use these cases in other classes to make a difference between the ViewControllers...

iOS - passing Sender (button) name to addSubview

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

Resources