I have a view controller, which creates 2 view controllers inside it. Inside each of these child controllers I wanted to create a property, which would be public, and accessible from the parent controller.
I am using the following method
TableViewController.h
#interface TableViewController : UITableViewController {
NSInteger projectId;
}
#property NSInteger projectId;
TableViewController.m
#implementation TableViewController
#synthesize projectId;
...
#end
I was then expecting the parent view controller to be able to create the child and access the parentId variable:
ParentViewController.h
#import "TableViewController.h"
#interface ParentViewController : UIViewController {
TableViewController* tableViewController;
}
#property (nonatomic, retain) TableViewController* tableViewController;
ParentViewController.m
#implementation ParentViewController
- (void)viewDidLoad {
self.tableViewController = [[TableViewController alloc] initWithNibName:#"TableViewController" bundle:nil];
self.tableViewController.projectId = 100;
}
However, the property projectId is not found, and returns the error - Request formember 'projectId'in something not a structure or a union.
If I change the line
self.tableViewController.projectId = 100;
to
[self.tableViewController setProjectId:100];
I get the warning - 'TableViewController' may not respond to '-setProjectId'
I am compiling this within XCode 4 preview 2, but also have the same issue within XCode 3.2
I am obviously missing something obvious, but cannot work out what it is.
Why can I not access this property?
Did you #import your .h files in the corresponding .m files?
Turns out that I had 2 copies of the TableViewController files. One had all my changes, the other was empty. I had initially created the file in the wrong directory and when I deleted it I had just removed it from the project.
Once I deleted these bogus files, everything worked fine.
Silly me!
Related
I'm testing out some MVVM pattern stuff and seem to have gotten myself confused. Hoping someone here can clarify things for me.
So, what I did was set up a project and added a class that is a subclass of NSObject and called it RootViewModel.
Gave it one method:
- (void) rootTest {
NSLog(#"Booyeah!");
}
In ViewController I imported RootViewModel and made an IBOutlet for it.
#import "ViewController.h"
#import "RootViewModel.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIButton *btnRunModel;
#property IBOutlet RootViewModel* myModel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.myModel rootTest];
}
#end
Then in Storyboard I dragged an Object into the ViewController scene, named it RootModel and connected it to the myModel property in ViewController.
Run the app and it works as expected, Booyeah gets logged.
So now here's where I got messed up. I wanted to set up a unit test. So working in the default unit test file I imported ViewController and made it a property and instantiated it in the set up.
#import <XCTest/XCTest.h>
#import "ViewController.h"
#interface ObjectiveVMMVTests : XCTestCase
#property (nonatomic, strong) ViewController* myViewController;
#end
#implementation ObjectiveVMMVTests
- (void)setUp {
[super setUp];
self.myViewController = [[ViewController alloc] init];
}
Then I tried to create a test where I call the rootTest method.
- (void) testRootModel {
[self.myViewController.myModel rootTest];
}
But I get a compiler error saying myViewController has no property myModel. I assumed it would be there, not sure where I messed this up.
In your unit test, you are saying:
#import "ViewController.h"
That's great. So now the unit test knows that this is a class. But that is not where the myModel property is declared. It is declared in ViewController.m, making this a private property.
Move the property declaration into ViewController.h to make it public so the unit test can see it.
Like #matt said, the IBOutlet is not part of the public interface of ViewController. It's private, hidden in the implementation (.m) file.
You have at least two viable options:
Add #property IBOutlet RootViewModel* myModel; to the ViewController.h file to make it part of the public interface;
Add an interface definition to the ObjectiveVMMVTests unit test file that'll satisfy the compiler:
#interface ViewController ()
#property IBOutlet RootViewModel* myModel;
#end
The implementation of the -(RootViewModel*)myModel getter is there anyway, the compiler just needs to know that ViewController does respond to the message. (You could use performSelector if you weren't interested in the returned object.)
I'm using Xcode to write an app in objective c. I am trying to pass data from a container view controller to the parent view controller using delegation. I have successfully passed the data to the parent view controller, but all of the documentation sets what I have sent to the .h header file in the .m implementation file using viewDidLoad or viewDidAppear. I was wondering, since the view is already present, if there is a way to detect that data has been changed in a view and automatically run a method or code to update the view with the new information. Something along the idea of didReceiveNewData or didEditExistingValues (of course those arent real methods). Thank you for your help!
Edit: What I have done so far:
I want to pass the data from MainFeedTableViewController to MainFeedViewController (The first is in a container inside of the second). I want to set the title of the custom navigation bar in MainFeedViewController to something described in the MainFeedTableViewController.
In the MainFeedTableViewController.m (the view sending data) I have:
#import "MainFeedTableViewController.h"
#import "FeedViewController.h"
#interface MainFeedTableViewController ()
#end
#implementation MainFeedTableViewController
- (IBAction)swipeLeftDetected:(UIGestureRecognizer *)sender {
UIStoryboard *mc = self.storyboard;
FeedViewController *fv = [mc instantiateViewControllerWithIdentifier:#"FeedViewController"];
fv.navigationBarTitleToSet = #"HOPING TO SET TITLE TO THIS";
[self performSegueWithIdentifier:#"MainToLocalFeed" sender:self];
}
and some other unrelated stuff..
In the MainFeedTableViewController.h I have:
#import <UIKit/UIKit.h>
#interface MainFeedTableViewController : UITableViewController
#end
In the MainFeedViewController.m (the one receiving the data) I have:
#import "FeedViewController.h"
#interface FeedViewController () <UINavigationBarDelegate>
#property (weak, nonatomic) IBOutlet UINavigationBar *navigationBar;
#end
#implementation FeedViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)setNavigationBarTitle:(NSString *)navigationBarTitle{
self.navigationItem.title = navigationBarTitle;
}
And in the MainFeedViewController.h I have:
#import <UIKit/UIKit.h>
#interface FeedViewController : UIViewController
#property NSString *navigationBarTitleToSet;
#end
I want to run the setNavigationBarTitle method with either data from the .h (navigationBarTitleToSet) or just from the sending view controller, if possible to run a method with delegation. Thanks a ton and I hope this is possible :)
It turns out I needed to add a second navigation bar to account for the container view, allowing me to navigate around the current stack with the parentViewController method and then navigationItem.title. For anyone who happens to find this with a container, make sure you add one immediately after the embed segue. I'm still not sure if you can use methods through delegation, but I can't ponder any situations where it would be necessary anymore, due to viewDidLoad. Thanks to #Tander for the help!
To start off with I am very new, (about 2.5 weeks) to programming in Objective-C and even newer to writing code for OS X cocoa apps. I am attempting to set the value of a NSTextField label in AppDelegate.m whose IBOutlet property exists in another class. I'm attempting to place this in the - (void)applicationDidFinishLaunching:(NSNotification *)aNotification{} section of AppDelegate.m so that the value of the NSTextField is set before the MainMenu.xib file is loaded and displayed on screen. Here is the following code that I have so far:
AppDelegate.m:
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
// Get Physical memory in MB
MemoryMonitoring *physicalMemoryObj = [[MemoryMonitoring alloc]init];
unsigned long long physicalMemoryValue = [physicalMemoryObj getPhysicalMemoryValue];
// Set the labels on the slider
RamdiskSize *sizeLabels = [[RamdiskSize alloc]init];
NSString *maxValue = [[NSString alloc] initWithFormat:#"%lluGB",(physicalMemoryValue / 1024)];
// This line is not doing what I had expected
[sizeLabels.textLabelSizeMax setStringValue:maxValue];
}
#end
MemoryMonitoring.h:
#import <Foundation/Foundation.h>
#interface MemoryMonitoring : NSObject
-(unsigned long long)getPhysicalMemoryValue;
#end
MemoryMonitoring.m:
#import "MemoryMonitoring.h"
#implementation MemoryMonitoring
-(unsigned long long)getPhysicalMemoryValue{
NSProcessInfo *pinfo = [NSProcessInfo processInfo];
return ([pinfo physicalMemory] /1024/1024);
}
#end
RamdiskSize.h:
#import <Foundation/Foundation.h>
#interface RamdiskSize : NSObject
#property (weak) IBOutlet NSTextField *textLabelSizeMax;
#end
RamdiskSize.m:
#import "RamdiskSize.h"
#import "MemoryMonitoring.h"
#implementation RamdiskSize
#synthesize textLabelSizeMax;
#end
As commented in my AppDelegate.m, the line in question is [sizeLabels.textLabelSizeMax setStringValue:maxValue];. My only other programming experience is from VBScript and as far as I can tell Objective-C uses dot syntaxing to access properties, so this line doesn't seem to be doing what I had expected it to do. If anyone could shed some light on how this is to be done properly, I would greatly appreciate the input.
There needs to be a UIViewController or a subclass of one involved. The textField must be part of a view hierarchy rooted with a view controller's view. Maybe start with a single view application template and add the text field to that view.
Then when that view controller sees viewWillAppear fire, it can ask the MemoryMonitoring class for the value and carry on setting it's own text field.
A good sign that you're on the right track is that you'll need to add virtually nothing to your app delegate code.
I've set up a LibraryDataController class that gets data from an sqlite database. This is its .h file:
#import <Foundation/Foundation.h>
#import <sqlite3.h>
#interface LibraryDataController : NSObject{
sqlite3 *database;
}
#property (nonatomic) NSMutableArray *masterPhotoList;
-(NSInteger)listCount;
-(NSString*)objectInListAtIndex:(NSUInteger)index;
-(void)createEditableCopyOfDatabaseIfNeeded;
-(void)initializeDefaultList;
-(void)closeDatabase;
#end
I also have a library view that has a LibraryDataController member:
#import <UIKit/UIKit.h>
#class LibraryDataController;
#interface LibraryViewController : UICollectionViewController
#property (nonatomic, strong) LibraryDataController *dataController;
#end
My problem occurs on the app startup where I load information from the sqlite database into a data controller (The init function does this successfully). Then I try to assign the database to the library view. The library view is the second view on a tab bar, and the tab bar controller is the root view.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UITabBarController *tabController = (UITabBarController *)self.window.rootViewController;
LibraryViewController *libraryView = [[tabController viewControllers] objectAtIndex:1];
LibraryDataController *aDataController = [[LibraryDataController alloc] init];
libraryView.dataController = aDataController;
return YES;
}
The error comes from the libraryView.dataController = aDataController; line. Any ideas about what my problem is? I'm thinking it may have to do with the UICollectionView as I am very new to it. This code is almost identical to code I used successfully with a UITableView and I was under the impression the two views were very similar. Thanks for your help.
The error message
[UICollectionViewController setDataController:]: unrecognized selector sent to instance 0x71a5170
shows that libraryView (which is the second view controller of the tab bar controller) is an instance of UICollectionViewController, and not LibraryViewController, as you expected.
The solution is to set the "Custom Class" of the view controller in the storyboard file to "LibraryViewController" (using the "Identity inspector).
For some reason, i'm not able to change or add text in my UITextView from code.
I have made Outlet and connected (All in IB) - nothing happens.
Even tried to add -setNeedsDisplay
Do i need to set some property ? - This i driving me nuts....
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITextViewDelegate> {
UITextView *textInfoView;
}
#property (nonatomic, retain) IBOutlet UITextView *textInfoView;
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
textInfoView.text = #"Test";
}
-(void)viewDidAppear:(BOOL)animated{
textInfoView.text = #"Test";
textInfoView.text = [textInfoView.text stringByAppendingString:#"Line2"];
[textInfoView setNeedsDisplay];
It seems, really strange that this code doesn't update the text inside the UITextView. I have tried and it works for me ... I think that you don't have correctly linked the Outlet in interface builder, or you don't have associated your view controller to your view ...
UPS - Got it...
Forgot to #synthesize...
For some reason i did not get warning or "setter error" in Xcode 4.4, maybe it's a version bug ?