I have a newbie question. How do you access a property from another class?
In my program, I have a view in which there are three levels available: easy, intermediate and difficult. I have three buttons with tags (I'm trying stuff, so I've called those buttons: try1, try2 and try3) and I have created an int that holds the tag of the button pressed. So, the h of my first file:
#property (strong, nonatomic) IBOutlet UIButton *try3;
#property (strong, nonatomic) IBOutlet UIButton *try2;
#property (strong, nonatomic) IBOutlet UIButton *try1;
#property int level;
-(IBAction)buttonClicked:(id)sender;
the method in the m file:
- (IBAction)buttonClicked:(id)sender {
self.level = [sender tag];
NSLog(#"tag is %d", self.level);
}
Now, in my other view controller, I want to access the property "level" to change something, like so:
if (level == 0) { do somtething
}
if (level == 1) { do somtething
}
etc...
Assuming this second view controller was presented by the first view controller, rather than trying to access the property of the first one, the second one should have its own property for level and the first view controller should populate that second view controller's property when it presents the second view controller.
If you search Stack Overflow for "pass data between view controllers" (or variations thereof), you'll find good examples. It depends a bit upon how the second view controller is presented (e.g. if you used a segue, you set the second view controller's property in the first view controller's prepareForSegue).
You need to implement a custom init for your embedded viewController, for instance:
EmbeddedViewController.h
#import <UIKit/UIKit.h>
#interface EmbeddedViewController : UIViewController
- (id)initWithLevel:(int)level;
#end
EmbeddedViewController.m
#import "EmbeddedViewController.h"
#interface EmbeddedViewController ()
#end
#implementation EmbeddedViewController {
int selectedLevel;
}
- (id)initWithLevel:(int)level {
if (self = [super init]) {
selectedLevel = level;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You will have to use this custom init from your firstViewController:
- (IBAction)buttonClicked:(id)sender {
self.level = [sender tag];
EmbeddedViewController *evc = [[EmbeddedViewController alloc] initWithLevel:[sender tag]];
[self.navigationController pushViewController:evc animated:YES];
}
Related
I am a very beginner of objective c and I wanted to create a scene about using 2 child view controller in a master view controller which includes MapKit and TableView. I ve searched it on internet for 1 day and but I couldn't make any of solutions since I failed in different steps of each suggestions. I know that there are a lot of way to pass data between View Controllers and I thought the best way is using delegation logic in this situation. (let me know if I am wrong please). By the way, I can update or move the cursor onto specific location WHEN I set a dummy button on MapKitViewController, so MapKit part is not where I am failing at. I am pretty sure that the problem is about communication between 2 View Controllers which are active at the same time.
Problem:
Updating MapView by clicking a table row of TableView which includes location coordinate detail. Both children view are set on a MasterViewController by 2 container views.
Tried so far:
Created a method "goToLocation:(Location*) location" in MapViewController and sent parameters to this method from TableViewController. =>(Lat and Long parameters received by goToLocation() but MapKitView doesn't get updated)
Tried to create a delegate logic between MapView and TableView.=>(Probably I couldn't create it properly. Please see below)
What I want :
I want to know what part I am doing wrong. Is there a easy or proper way to achieve that ?
TableViewController.h
#class TableViewController; //define class
#protocol TableViewDelegate <NSObject> //define delegate protocol
//define delegate method to be implemented within another class
- (void) locationSelected: (TableViewController *) sender object:(Location *) location;
#end
#property (nonatomic, weak) id <TableViewDelegate> delegate; //define TableViewVCDelegate as delegate
TableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self notifyNow:[_locations objectAtIndex:indexPath.row]];
}
- (void) notifyNow:(Location *) location {
//this will call the method implemented in your other class
[self.delegate locationSelected:self object:(Location *) location];
NSLog(#"Selected: %#",[location name]);
}
MapViewController.h
//make it a delegate for TableViewVCDelegate
#interface MapViewController : UIViewController <TableViewDelegate>
MapViewController.m
- (void)viewDidLoad {
_tableViewController = [[TableViewController alloc]init];
_tableViewController.delegate = self; //set its delegate to self somewhere
}
!!_ This Method Doesn't Get Triggered _!!
- (void)locationSelected:(TableViewController *)sender object:(Location *)location {
NSLog(#"%# is great!", [location name]);
}
MainViewController.m (Master/Root View Controller)
#import "MainViewController.h"
#import "MapViewController.h"
#import "TableViewController.h"
#interface MainViewController ()
#property (strong, nonatomic) IBOutlet UIView *topCont;
#property (strong, nonatomic) IBOutlet UIView *bottomCont;
#property (strong, nonatomic) IBOutlet UIView *safeArea;
#end
#implementation MainViewController
MapViewController *mapViewVC;
TableViewController *tableViewVC;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.navigationController setNavigationBarHidden:YES animated:YES];
tableViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TableViewController"];
mapViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TopChildVC"];
[self addChildViewController:tableViewVC];
[tableViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.bottomCont addSubview:tableViewVC.view];
[tableViewVC didMoveToParentViewController:self];
[self addChildViewController:mapViewVC];
[mapViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.topCont addSubview:mapViewVC.view];
[mapViewVC didMoveToParentViewController:self];
}
#end
Im new to programming with objective C and am working on moving data between View controllers. I am wondering if Bi-directional flow of data (variables) between ViewControllers is possible.
I can move data backwards (to the presentingViewController / sourceViewController) however i cannot move data forward (to the presentedViewController / destinationViewController).
I have made a simple case scenario (involving strings to get a principle of the idea) of this and it involves updating a UItextField on the destinationViewController using a UILabel in the sourceViewController and vice-versa.
I CANNOT update the UITextField using the UILabel, but can update the UILabel using the UITextField.
I have made Logs of different statements to track the variable values however when I switch ViewControllers the variables Data returns to null even if they are marked as strong.
Can you please offer any guidance, its been tearing away at my mind, or am I missing something obvious? I don't get why I keep getting a (null) value (in my NSLog) when I switch ViewControllers.
My sourceViewController / presentingViewController is named "ViewController."
My destinationViewController / presentedViewController is named "Gears2ViewController".
I have attached my code files below:
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *outputLabel;
- (IBAction)ExitToHere:(UIStoryboardSegue *)sender;
#end
ViewController.m:
#import "ViewController.h"
#import "Gear2ViewController.h"
#interface ViewController ()
- (IBAction)changeItem:(id)sender;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)changeItem:(id)sender {
Gear2ViewController *G2VC=[[Gear2ViewController alloc] init];
G2VC.peterSido=[NSString stringWithFormat:#"%#",self.outputLabel.text];
[self performSegueWithIdentifier:#"toGear2" sender:self];
NSLog(#"ViewController UILabel reads %#",G2VC.peterSido);
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
}
- (IBAction)ExitToHere:(UIStoryboardSegue *)sender {
}
#end
Gears2ViewController.h:
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface Gear2ViewController : UIViewController
#property (strong, nonatomic) NSString *peterSido;
#end
Gears2ViewController.m:
#interface Gear2ViewController ()
#property (strong, nonatomic) IBOutlet UITextField *updatedOutput;
- (IBAction)updateOutput:(id)sender;
#end
#implementation Gear2ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"Gears2ViewController ViewDidAppear reads %#",self.peterSido);
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Gears2ViewController ViewDidLoad responds %#",self.peterSido);
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)updateOutput:(id)sender {
self.peterSido = self.updatedOutput.text;
((ViewController *)self.presentingViewController).outputLabel.text = self.peterSido;
NSLog(#"Gears2View Controller updating ViewController UILabel reads %#",self.peterSido);
}
#end
NSLog:
2015-06-29 18:52:58.798 testerBeta[21735:645772] Gears2ViewController ViewDidLoad responds (null)
2015-06-29 18:52:58.799 testerBeta[21735:645772] ViewController UILabel reads I like Pie
2015-06-29 18:52:59.317 testerBeta[21735:645772] Gears2ViewController ViewDidAppear reads (null)
2015-06-29 18:53:12.651 testerBeta[21735:645772] Gears2View Controller updating ViewController UILabel reads No I dont
Quite Lengthy but Thanks in Advance!!!
You want to pass the data in prepareForSegue:, like so:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)__unused sender
{
if ([[segue identifier] isEqualToString:#"toGear2"])
{
Gear2ViewController *controller = (Gear2ViewController *)segue.destinationViewController;
controller.peterSido = self.outputLabel.text;
}
}
The reason why is that the segue instantiates the presented view controller for you, and you then set the property of the instantiated view controller which the segue will present.
To pass the data back, you can use an unwind segue, which can get the value from the presented view controller's property.
- (IBAction)unwindFromGear2:(UIStoryboardSegue *)segue
{
Gear2ViewController *controller = (Gear2ViewController *)segue.sourceViewController;
self.outputLabel.text = controller.peterSido;
}
This is the proper way to pass data back and forth via segues. Gear2ViewController shouldn't be setting properties on its presentingViewController.
Update:
The preferred way to test that a property isn't nil is like this:
if (self.peterSido)
{
self.updatedOutput.text = self.peterSido;
}
else // No need for if test here
{
self.updatedOutput.text = #"";
}
That's the long form, but the assignment and if test can be more concisely written as:
self.updatedOutput.text = self.peterSido ?: #"";
When you declare any variable as #property then you need to synthesize it in .m file .
You have declared your outputLabel as #property but you missed to synthesize it in .m file.
When you synthesize any variable then it allows you to get and set the values to it .
Do it it will help you.
Thank you.
First I'll give you a short overview.
I'm ...
creating a new cocoa project
customizing the AppDelegate (see listing 1)
adding a "Custom View" to my MainMenu.xib
creating a new Cocoa Class (NSViewController + XIB) in the project, calling it MyTableViewController.*
adding a "Table View" to the recently added ViewController, like described in LINK
the code of my MyTableViewController can be seen in listing 2
Now to my problem.
The table and it's content is shown.
But if I select an item of the table and press a button (connected to (IBAction)action:(id)sender) on this subview, <NSIndexSet: 0x60000022c100>(no indexes) is shown in the output (see: selectedColumnIndexes).
After experimenting a while, I found out that there are two instances of the MyTableViewController class.
Can someone please explain me why there are two instances and help me to fix this problem.
Thx
listing 1:
// FILE: AppDelegate.h
#import <Cocoa/Cocoa.h>
#class MyTableViewController;
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (nonatomic, assign) NSViewController * currentViewController;
#property (nonatomic, strong) MyTableViewController * myTableViewController;
#property (weak) IBOutlet NSView *myview;
#end
// FILE: AppDelegate.m
#import "AppDelegate.h"
#import "MyTableViewController.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self changeViewController];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
return YES;
}
- (void)changeViewController
{
if ([self.currentViewController view] != nil) {
[[self.currentViewController view] removeFromSuperview];
}
switch (0) {
case 0:
default:
if (self.myTableViewController == nil) {
_myTableViewController = [[MyTableViewController alloc] initWithNibName:#"MyTableViewController" bundle:nil];
}
self.currentViewController = self.myTableViewController;
NSLog(#"EndView");
break;
}
[self.myview addSubview:[self.currentViewController view]];
[[self.currentViewController view] setFrame:[self.myview bounds]];
[self.currentViewController setRepresentedObject:[NSNumber numberWithUnsignedInteger:[[[self.currentViewController view] subviews] count]]];
[self didChangeValueForKey:#"viewController"];
NSLog(#"ViewController changed");
}
#end
listing 2:
// FILE: MyTableViewController.h
#import <Cocoa/Cocoa.h>
#interface MyTableViewController : NSViewController
#property (weak) IBOutlet NSTableView *tview;
- (IBAction)action:(id)sender;
#end
// FILE: MyTableViewController.m
#import "MyTableViewController.h"
#import "AppDelegate.h"
#interface MyTableViewController ()
#end
#implementation MyTableViewController
- (NSUInteger)numberOfRowsInTableView:(NSTableView *)tableViewObj {
return 2;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
if ([tableView tableColumns][0] == tableColumn) {
return #"bla";
} else if ([tableView tableColumns][1] == tableColumn) {
return #"blub";
}
NSLog(#"dropped through tableColumn identifiers");
return NULL;
}
- (IBAction)action:(id)sender {
// selectedColumnIndexes
NSLog(#"%#", [self.tview selectedColumnIndexes]);
}
#end
Two instances of a class, when you only expect/want one, can be caused by failing to appreciate that objects in your xib files are themselves actual automatically-generated instances of that class.
Have you dragged a blue cube into either of your xib files and set it's class to your view controller subclass? If you have, then this will account for one of the objects that you're seeing - Apple's machinery creates it on your behalf. The second object is the one created by you, in code, in your changeViewController method.
I get the impression that you're simply trying to create a window, which contains an NSTableView, which in turn gets its data from your own NSViewController subclass. Is this correct? If it is then you should do away with the second xib file, and instead just use the xib created for you when you created the project.
In Brief
Drag a blue object cube into the Interface Builder dock and set it's subclass to MyTableViewController using the Identity Inspector.
With your view controller blue-cube selected go to the Connections Inspector and drag from the view option to your table view - you must make sure you're dragging to the table view, not the scroll view or clip view that encloses it.
Select the table view (again, make sure it really is the table view you've selected), and go to it's Connections Inspector. Drag from the data source and delegate options to your blue view-controller cube.
Implement the relevant data-soruce methods
Hint: If you aren't sure which inspector is which, open the right sidebar in Xcode and select a xib file from the left file-viewer sidebar. Sit the cursor over each of the icons at the top of the right sidebar and the tool tip will tell you which is which.
Update
A good way of identifying specific objects, and something that can assist with debugging, is to set their identifier. In the attributes inspector, this is the restoration ID. Do this for your NSTableView instance, and for your NSTableColumn instances. Then, in your data-source methods do some logging with them - for instance does the table view passed as the first argument for this methods have the expected identifier, what about the table columns?
I created a ViewController in StoryBoard and linked it with the GamePanelViewController class.
Furthermore I linked the view of the ViewController with a second class called GamePanel which extends of UIView.
How can I access to the view which is created automatically by StoryBoard to perform some methods I implemented to the GamePanel class which is linked to the view by identity inspector?
GamePanel.h:
#import <UIKit/UIKit.h>
#import "Plattform.h"
#interface GamePanel : UIView
#property (strong, nonatomic) NSMutableArray *plattforms;
- (void)initGamePanel;
- (void)drawPlattforms:(CGContextRef)gc;
#end
GamePanel.m:
#import "GamePanel.h"
#implementation GamePanel
#synthesize plattforms;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)initGamePanel{
self.backgroundColor = [UIColor redColor];
self.plattforms = [NSMutableArray new];
// Initialization code
for(int i = 0;i<8;i++){
for(int j = 0;j<6;j++){
[self.plattforms addObject:[[Plattform alloc] initWithXCord:(14+j*37) andYCoord:(58+14+i*37)]];
NSLog(#"Init");
}
}
}
If you have actually linked the viewController's view property with your custom view, then you can simply use self.view within you viewController to access it.
If you have just dragged a UIView onto your viewController in your storyboard (and not linked it to the viewController's view property), you will need to create an IBOutlet for it (control-drag from the view to your interface, and give it a name), then you can reference it with the name you give it e.g. self.myView.
Ok, so I'm a relative noob with Objective-C/iOS programming, so hopefully someone with more knowledge here can help me out.
I have an iPad application using the SplitViewController template (with Core Data). I created another UIViewController (with xib file) called PlayerViewController. This View has several UILabel components on it.
I have a list of players that show up in the RootViewController (UITableView) and when you select a player, I programmatically create a PlayerViewController (in DetailViewController), pass it the NSManagedObject that was passed to the DetailViewController, try to set the text of one of the labels on the PlayerViewController's view, and then add it as a subview to the DetailViewController.
All of this works great except for the setting the text of the label on the PlayerViewController's view. I'm not sure what I'm doing wrong. I have used NSLog to confirm that the NSManagedObject is not nil and that the NSManagedObject property I'm trying to use has the correct text.
I'm at a loss here. Any help would be greatly appreciated. (Code follows):
This method is in the DetailViewController.m file:
- (void)configureView {
// Update the user interface for the detail item.
PlayerViewController *player = [[PlayerViewController alloc] init];
player.player = detailItem;
[self.view addSubview:player.view];
}
This method is called when the user selects an item from the RootViewController (This functionality, calling of configureView, is setup by the template and I haven't changed it).
Setting the player property of the PlayerViewController to object detailItem is handled in the setPlayer method of that class.
- (void)setPlayer:(NSManagedObject *)managedObject {
if (player != managedObject) {
[player release];
player = [managedObject retain];
// Update the view.
[self configureView];
}
}
I then have a configureView method as well in PlayerViewController that sets the text of the label:
- (void)configureView {
nickName.text = [[player valueForKey:#"Nickname"] description];
NSLog(#"Nickname %#", [[player valueForKey:#"Nickname"] description]);
NSLog(#"Nickname %#", nickName.text);
}
Ok, so the first NSLog statement prints the desired value, but the text of the UILabel (called nickName) returns nil.
The following is the full PlayerViewController.h & .m files:
PlayerViewController.h:
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface PlayerViewController : UIViewController {
NSManagedObject *player;
IBOutlet UILabel *nickName;
IBOutlet UILabel *goalCount;
IBOutlet UILabel *assistCount;
IBOutlet UILabel *timeInGame;
}
#property (nonatomic, retain) IBOutlet UILabel *nickName;
#property (nonatomic, retain) IBOutlet UILabel *goalCount;
#property (nonatomic, retain) IBOutlet UILabel *assistCount;
#property (nonatomic, retain) IBOutlet UILabel *timeInGame;
#property (nonatomic, retain) NSManagedObject *player;
#end
PlayerViewController.m:
#import "PlayerViewController.h"
#implementation PlayerViewController
#synthesize nickName, goalCount, assistCount, timeInGame, player;
#pragma mark -
#pragma mark Managing the detail item
/*
When setting the player item, update the view
*/
- (void)setPlayer:(NSManagedObject *)managedObject {
if (player != managedObject) {
[player release];
player = [managedObject retain];
// Update the view.
[self configureView];
}
}
- (void)configureView {
nickName.text = [[player valueForKey:#"Nickname"] description];
NSLog(#"Nickname %#", [[player valueForKey:#"Nickname"] description]);
NSLog(#"Nickname %#", nickName.text);
}
/*
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
I'm sure I'm just missing something trivial, but I can't figure it out, and haven't been able to find any answers searching the web.
Thanks for any help!
Ok, so after playing with this for a bit and searching and searching around, I have gotten the answer to my problem. It turns out all the code I had was fine except the location of one statement. My call to configureView in PlayerViewController.m needed to be in viewDidLoad() not in the setPlayer() method. It all works great now.
Change the configureView method to that :
- (void)configureView {
nickName.text = (NSString*)[player valueForKey:#"Nickname"];
}
Yes, better place to call method is
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self configureView];
}
(void)setPlayer:(NSManagedObject *)managedObject called before your nib files loaded.