I have an app, where I added simple images (posters) with movie names. But after run, I can see only empty table and an error in console:
Illegal NSTableView data source (). Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:
Now, in my .xib file I have Table View with dataSource and delegate linked to the File's Owner. Here's the code:
MasterViewController.m
#import "MasterViewController.h"
#import "NSObject+IntengineMovieData.h"
#import "NSObject+IntengineMovieDoc.h"
#interface MasterViewController ()
#end
#implementation MasterViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
- (NSView *)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
if([tableColumn.identifier isEqualToString:#"MoviesColumn"]) {
IntengineMovieDoc *movieDoc = [self.movies objectAtIndex:row];
cellView.imageView.image = movieDoc.thumbImage;
cellView.textField.stringValue = movieDoc.data.title;
return cellView;
}
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [self.movies count];
}
#end
What that problem means and how can I fix it in my case?
EDIT 1: I have MasterViewController.xib as my Main Interface. I loaded it by this:
AppDelegate.m
#import "AppDelegate.h"
#import "NSObject+IntengineMovieDoc.h"
#include "MasterViewController.h"
#interface AppDelegate ()
#property (nonatomic, strong) IBOutlet MasterViewController *masterViewController;
#property (assign) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.masterViewController = [[MasterViewController alloc]
initWithNibName:#"MasterViewController" bundle:nil];
IntengineMovieDoc *movie1 = [[IntengineMovieDoc alloc] initWithTitle:#"The Godfather" rating:5 thumbImage:[NSImage imageNamed:#"TheGodfatherThumb.jpg"] fullImage:[NSImage imageNamed:#"TheGodfather.jpg"]];
IntengineMovieDoc *movie2 = [[IntengineMovieDoc alloc] initWithTitle:#"Tree of Life" rating:4 thumbImage:[NSImage imageNamed:#"TreeOfLifeThumb.jpg"] fullImage:[NSImage imageNamed:#"TreeOfLife.jpg"]];
IntengineMovieDoc *movie3 = [[IntengineMovieDoc alloc] initWithTitle:#"Taxi Driver" rating:5 thumbImage:[NSImage imageNamed:#"TaxiDriverThumb.jpg"] fullImage:[NSImage imageNamed:#"TaxiDriver.jpg"]];
NSMutableArray *movies = [NSMutableArray arrayWithObjects: movie1, movie2, movie3, nil];
self.masterViewController.movies = movies;
[self.window.contentView addSubview:self.masterViewController.view];
self.masterViewController.view.frame = ((NSView*)self.window.contentView).bounds;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end
Related
I'm working on Xcode 10.0 - macOS 10.14 (Mojave). I was doing an app for my school project that shows the list of movies in rows with their posters and rating stars. Whenever I'm trying to start my application, I'm getting errors:
[default] Unable to load Info.plist exceptions (eGPUOverrides)
But it seems that is the system's problem (as I read on Apple forums)... And the second error:
Failed to connect (view) outlet from (NSApplication) to (NSView): missing setter or instance variable
Are these errors somewhat connected? I was looking at the .xib file, trying to set the Main.storyboard as Main Interface but there I have empty view... With the .xib file I have nothing - app starts, but only these two errors show up. Here's the code:
MasterViewController.m
#import "MasterViewController.h"
#import "NSObject+IntengineMovieData.h"
#import "NSObject+IntengineMovieDoc.h"
#interface MasterViewController ()
#end
#implementation MasterViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
if([tableColumn.identifier isEqualToString:#"MoviesColumn"]) {
IntengineMovieDoc *movieDoc = [self.movies objectAtIndex:row];
cellView.imageView.image = movieDoc.thumbImage;
cellView.textField.stringValue = movieDoc.data.title;
return cellView;
}
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [self.movies count];
}
#end
AppDelegate.m
#import "AppDelegate.h"
#import "NSObject+IntengineMovieDoc.h"
#include "MasterViewController.h"
#interface AppDelegate ()
#property (nonatomic, strong) IBOutlet MasterViewController *masterViewController;
#property (assign) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.masterViewController = [[MasterViewController alloc]
initWithNibName:#"MasterViewController" bundle:nil];
IntengineMovieDoc *movie1 = [[IntengineMovieDoc alloc] initWithTitle:#"The Godfather" rating:5 thumbImage:[NSImage imageNamed:#"TheGodfatherThumb.jpg"] fullImage:[NSImage imageNamed:#"TheGodfather.jpg"]];
IntengineMovieDoc *movie2 = [[IntengineMovieDoc alloc] initWithTitle:#"Tree of Life" rating:4 thumbImage:[NSImage imageNamed:#"TreeOfLifeThumb.jpg"] fullImage:[NSImage imageNamed:#"TreeOfLife.jpg"]];
IntengineMovieDoc *movie3 = [[IntengineMovieDoc alloc] initWithTitle:#"Taxi Driver" rating:5 thumbImage:[NSImage imageNamed:#"TaxiDriverThumb.jpg"] fullImage:[NSImage imageNamed:#"TaxiDriver.jpg"]];
NSMutableArray *movies = [NSMutableArray arrayWithObjects: movie1, movie2, movie3, nil];
self.masterViewController.movies = movies;
[self.window.contentView addSubview:self.masterViewController.view];
self.masterViewController.view.frame = ((NSView*)self.window.contentView).bounds;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end
EDIT 1: Ok, it seems that I have one extra outlet in Referencing Outlets in View Panel. I didn't add that, but after remove this link, app won't show either. Now it shows only this error:
[default] Unable to load Info.plist exceptions (eGPUOverrides)
There is no need to create an IBOutlet for your view controller since you are instantiating a new instance anyway inside your applicationDidFinishLaunching.
Additionaly, you should make your masterViewController as the rootViewController of your window like this:
self.window.rootViewController = self.masterViewController;
[self.window makeKeyAndVisible];
You might have to create a new window first:
self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
I am making a table view application and it will not recognize the type studentController in the studentAddController.h. I have a previous project that I looked at and cannot figure out why it can not recognize the type name.
Here is the code in the studentController.h
#import <UIKit/UIKit.h>
#import "studentCells.h"
#import "AssignmentsController.h"
#import "studentAddController.h"
#interface studentController : UITableViewController {
NSMutableArray *studentArray;
}
- (IBAction)addStudentButton:(id)sender;
- (void)insertNewRow:(NSDictionary *)studentDictionary;
#property (nonatomic, retain) UITableView *studentTableView;
#end
Here is the code in the studentController.m
#import "studentController.h"
#implementation studentController
-(id)initWithStyle:(UITableViewStyle)style {
self =[super initWithStyle:style];
if (self) {
}
return self;
}
-(void)viewDidLoad {
[super viewDidLoad];
studentArray = [[NSMutableArray alloc] initWithObjects: nil];
}
- (IBAction)addStudentButton:(id)sender {
studentAddController *studentAddView =[self.storyboard instantiateViewControllerWithIdentifier:#"studentAddView"];
[self.navigationController pushViewController:studentAddView animated:YES];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [studentArray count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *studentCellIdentifier = #"studentCell";
studentCells *cell = [tableView dequeueReusableCellWithIdentifier:studentCellIdentifier forIndexPath:indexPath];
NSDictionary *studentDictionary = [studentArray objectAtIndex:[indexPath row]];
[cell.firstNameCellLabel setText:[studentDictionary objectForKey:#"First Name"]];
[cell.lastNameCellLabel setText:[studentDictionary objectForKey:#"Last Name"]];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
AssignmentsController *assignmentView = [self.storyboard instantiateViewControllerWithIdentifier:#"assignmentView"];
[self.navigationController pushViewController:assignmentView animated:YES];
}
-(void)insertNewRow:(NSDictionary *)studentDictionary {
[studentArray addObject:studentDictionary];
[self.tableView beginUpdates];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[studentArray count] -1 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[self.tableView endUpdates];
}
#end
Here is the code for studentAddController.h
#import <UIKit/UIKit.h>
#import "studentController.h"
#import "studentAddController.h"
#interface studentAddController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *firstNameField;
#property (weak, nonatomic) IBOutlet UITextField *lastNameField;
- (IBAction)doneButton:(id)sender;
- (IBAction)keyboardResign:(id)sender;
#property (nonatomic, strong) studentController *parentTableVC;
#end
Here is the code in the studentAddController.m
#import "studentAddController.h"
#implementation studentAddController
- (IBAction)doneButton:(id)sender {
NSDictionary *studentDictionary =#{ #"First Name" : [self.firstNameField text],
#"Last Name" : [self.lastNameField text]
};
[self.parentTableVC insertNewRow:studentDictionary];
[self.navigationController popViewControllerAnimated:YES];
}
- (IBAction)keyboardResign:(id)sender {
[self resignFirstResponder];
}
#end
You have a circular reference. In studentController.h you import studentAddController.h and in studentAddController.h you import studentController.h. Try making these changes to your headers:
studentController.h
#import <UIKit/UIKit.h>
#import "studentCells.h"
#import "AssignmentsController.h"
studentController.m
#import "studentController.h"
#import "studentAddController.h"
studentAddController.h
#import <UIKit/UIKit.h>
#import "studentController.h"
I would also note that naming standards would dictate your classes are PascalCase not camelCase. Only your variables and methods should be camelCase.
I would also note that you didn't need to import studentAddController in studentController.h so I just moved it to the .m. If you actually needed to import studentAddController then you would do a forward class declaration to avoid a circular reference. Like this:
#import <UIKit/UIKit.h>
#import "studentCells.h"
#import "AssignmentsController.h"
#class studentAddController;
I want to make a NSTableView with customs NSTableCellView, here's what I've done so far :
AppDelegate.h :
#import <Cocoa/Cocoa.h>
#import "TheView.h"
#interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource, NSTableViewDelegate>
#property (weak) IBOutlet NSTableView *tableView;
#property (copy) NSMutableArray *tableContent;
#property (assign) IBOutlet NSWindow *window;
-(IBAction)addRow:(id)sender;
#end
AppDelegate.m :
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize tableContent;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
tableContent = [[NSMutableArray alloc]init];
}
-(id)init{
self = [super init];
if (self) {
tableContent = [[NSMutableArray alloc]init];
}
return self;
}
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
NSLog(#"%#",tableContent); //Here tableContent is empty
return [tableContent count];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSView *result = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
TheView *view = [tableContent objectAtIndex:row];
result = view;
return result;
}
-(IBAction)addRow:(id)sender {
TheView *view = [[TheView alloc]init];
[tableContent addObject:view];
NSLog(#"%#",tableContent); //Here tableContent result with the correct number of objects
[_tableView reloadData];
}
But when I try to add an object, nothing happens... TheView is a subclass of NSTableCellView, it's drawing a simple rect.
I've of course added NSTableViewDataSource and NSTableViewDelegate.
UPDATE 2
After further researches in my code, I found that in numberOfRowsInTableView, tableContent resulted empty or with the number of object I initialized my array with...
The views your creating seem to have no size. You could try replacing the view initialization, and use initWithFrame instead of init
[[TheView alloc]initWithFrame:CGRectMake(0, 0, 100, 20)];
Also, you can try set in fame on the viewForTableColumn method.
In my Storyboard, I am using a UINavigationController with a navigation bar to present a view controller. This view controller, ViewController2, is pushed to the stack by tapping a UIBarButtonItem that I placed in the navigation bar. I made this connection in Interface Builder by dragging from the bar button to the ViewController2 scene and selecting "push" for the segue. To return back to the first view controller, I have an IBAction that calls the popViewControllerAnimated method. I'm using the delegate method to send data back to the first view controller but the string from ViewController2 is not being sent back to ViewController.
How can I pass the text entered in ViewController2 back to the first ViewController using the delegate method?
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
ViewController.m
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController () <UITableViewDelegate,UITableViewDataSource,Vc2Delegate>
#property (strong,nonatomic) NSArray *tableArray;
#property (weak,nonatomic) IBOutlet UITableView *listTableView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.listTableView setDelegate:self];
[self.listTableView setDataSource:self];
self.tableArray = #[#"item 1",#"item 2",#"item 3"];
ViewController2 *vc2 = [[ViewController2 alloc] init];
[vc2 setDelegate:self];
}
# pragma mark - TableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.tableArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ItemCell" forIndexPath:indexPath];
cell.textLabel.text = [self.tableArray objectAtIndex:indexPath.row];
return cell;
}
#pragma mark - Delegate Method
- (void)returnFromVc2:(NSString *)userString {
NSLog(#"delegate text is %#",userString);
}
#end
ViewController2.h
#import <UIKit/UIKit.h>
#protocol Vc2Delegate <NSObject>
- (void)returnFromVc2:(NSString *)userString;
#end
#interface ViewController2 : UIViewController
#property (weak,nonatomic) id <Vc2Delegate> delegate;
#end
ViewController2.m
#import "ViewController2.h"
#interface ViewController2 ()
#property (weak,nonatomic) IBOutlet UITextField *textField;
#end
#implementation ViewController2
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)toPreviousVC:(id)sender {
NSLog(#"user text is: %#",self.textField.text);
[self.delegate returnFromVc2:self.textField.text];
[self.navigationController popViewControllerAnimated:YES];
}
#end
You set the delegate to a local variable that won't be the same instance as the one you push.
- (void)viewDidLoad {
....
ViewController2 *vc2 = [[ViewController2 alloc] init];
[vc2 setDelegate:self];
....
}
You should also make a habit of checking if your delegate can respond to the message sent.
- (IBAction)toPreviousVC:(id)sender {
NSLog(#"user text is: %#",self.textField.text);
if ( [self.delegate respondsToSelector(#selector(returnFromVc2:))] ) {
[self.delegate returnFromVc2:self.textField.text];
}
[self.navigationController popViewControllerAnimated:YES];
}
I have a simple utility app, with a MainViewController.m & h and a FlipsideViewController.m & h. Within my storyboard I have a button on MainViewController. I want to be able to click the button and run a method in FlipsideViewController.m is this possible? this is my first app and I am a total novice. all comments / suggestion welcome.
enter code here
i have this in my FlipsideViewController.m this is what i want to call when i click the button.
- (void)SaveFPQData
{
NSLog(#"Data Saved");
}
and this is what i have in MainViewController.m
- (IBAction)saveButton:(id)sender
{
}
This is the code I have so far;
MainViewController.h
#import "FlipsideViewController.h"
#import "sqlite3.h"
#import "FPQCheck.h"
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate>
#property (weak, nonatomic) IBOutlet UITextField *nameField;
#property (weak, nonatomic) IBOutlet UITextField *checkField;
#property (weak, nonatomic) IBOutlet UITextField *commentsField;
#property (weak, nonatomic) FlipsideViewController *flipsidecontroller;
- (IBAction)saveButton:(id)sender;
- (IBAction)showHistoryButton:(id)sender;
#end
MainViewController.m
#import "MainViewController.h"
#import "FlipsideViewController.h"
#import "sqlite3.h"
#interface MainViewController ()
#end
#implementation MainViewController
- (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.
}
#pragma mark - Flipside View
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showAlternate"]) {
[[segue destinationViewController] setDelegate:self];
}
}
- (IBAction)saveButton:(id)sender
{
[self.flipsidecontroller SaveFPQData];
//[[NSNotificationCenter defaultCenter] postNotificationName:#"SaveFPQData" object:nil];
}
- (IBAction)showHistoryButton:(id)sender
{
}
#end
FlipSideViewController.h
#import <UIKit/UIKit.h>
#import "FPQCheck.h"
#class FlipsideViewController;
#protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
#end
#interface FlipsideViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
#property (weak, nonatomic) id <FlipsideViewControllerDelegate> delegate;
-(void)SaveFPQData;
- (IBAction)done:(id)sender;
- (IBAction)deleteEntry:(id)sender;
#end
FlipSideViewController.m
#import "FlipsideViewController.h"
#import "MainViewController.h"
#interface FlipsideViewController ()
{
NSMutableArray *arrayOfCheck;
sqlite3 *fpqDB;
NSString *dbPathString;
}
#end
#implementation FlipsideViewController
- (void)viewDidLoad
{
/*
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(SaveFPQData)
name:#"SaveFPQData"
object:nil];
*/
[super viewDidLoad];
arrayOfCheck = [[NSMutableArray alloc]init];
[self creatOrOpenDB];
[[self myTableView]setDelegate:self];
[[self myTableView]setDataSource:self];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)SaveFPQData
{
NSLog(#"Data Saved");
}
-(void)creatOrOpenDB
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSString *docPath = [path objectAtIndex:0];
dbPathString = [docPath stringByAppendingPathComponent:#"FPQ.db"];
char *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dbPathString]) {
const char *dbPath = [dbPathString UTF8String];
//create db
if (sqlite3_open(dbPath, &fpqDB)==SQLITE_OK) {
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS FPQ (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, CHECK INTEGER, COMMENTS TEXT)";
sqlite3_exec(fpqDB, sql_stmt, NULL, NULL, &error);
sqlite3_close(fpqDB);
}
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Actions
- (IBAction)done:(id)sender
{
[self.delegate flipsideViewControllerDidFinish:self];}
- (IBAction)deleteEntry:(id)sender {
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayOfCheck count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell){
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
FPQCheck *fpqCheck = [arrayOfCheck objectAtIndex:indexPath.row];
NSString *nameANDcheck = [NSString stringWithFormat:#"%#%d", fpqCheck.name, fpqCheck.check];
cell.textLabel.text = nameANDcheck;
cell.detailTextLabel.text = fpqCheck.comments;
return cell;
}
#end
You have mainly two ways:
add a property (eg. self.flipSideController) to your MainViewController to store a reference to the FlipsideViewController; then call SaveFPQData though it (eg. [self.flipSideController SaveFPQData]; or
use notification center to post a notification from saveButton: that triggers SaveFPQData; this would go like this:
//-- in flipsidecontroller `viewDidLoad`:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(SaveFPQData)
name:#"SaveFPQData"
object:nil];
//-- in saveButton:
[[NSNotificationCenter defaultCenter] postNotificationName:#"SaveFPQData" object:nil];
The second method is the simplest to implement, IMO, and it allows for the loosest coupling, at the expense of some clock cycles.
EDIT:
It is not entirely clear to me what you are trying to do (specifically, I don't understand fully how you can push the button in MainViewController once you FlipsideViewController is displayed; on the other hand, if you do not segue to the FlipsideViewController, then it is not there, so you cannot send a message to it), anyway you could try and initialise your self.flipsideViewController property in prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showAlternate"]) {
UIViewController* controller = [segue destinationViewController];
[controller setDelegate:self];
if ([controller isKindOfClass:[FlipsideViewController class]])
self.flipsideViewController = (id)controller;
}
}
after doing that, your MainViewController will be able to send the saveFPQ message to the FlipsideViewController.
If you mean you would like to send the saveFPQ message before segue-ing to the FlipsideViewController, you should make the saveButton segue to it and the call the saveFPQ method.
What I suspect is you need some kind of "model" object accessible both from the main view and the flipside view controller.
Hope this helps.