Please explain me how to control a Table View in Cocoa? - xcode4

I search Google to find a good answer but the most tutorials are shown in previous Xcode Versions...
Also, I don't want to drag-n-drop cells from the Interface Builder, but to control the Table View programmatically (from an NSObject subclass file).
What I currently do is this: 1. Create a file named tableController.h that is a subclass of NSObject.
2. I create an NSObject Object in my Nib File (and set it as a subclass of tableController).
3. I drag a Table View to my window.
4. I CTRL+Drag from the Table View to my tableController.h so to create the outlet "tableView"
5. I create these functions in the interface file:
-(int)numberOfRowsInTableView:(NSTableView *)cocoaTV;
-(id)tableView:(NSTableView *)cocoaTV:objectValueForTableCollumn:(NSTableColumn *)tableCollumn row:(int)row;
6. I implement the functions like this:
-(int)numberOfRowsInTableView:(NSTableView *)cocoaTV{
return 5;
}
-(id)tableView:(NSTableView *)cocoaTV:objectValueForTableCollumn:(NSTableColumn *)tableCollumn row:(int)row{
NSArray *tvArray = [[NSArray alloc]initWithObjects:#"1",#"2",#"3",#"4",#"5", nil];
NSString *v = [tvArray objectAtIndex:row];
return v;
}
Then I CTRL+Drag from the Object in the Interface Builder to the Table View to set the dataSource and to set it as delegate.
When I build and Run the App it shows that it has created the 5 Rows but in every cell in every column it says "Table View Cell".
Any help would be appreciated....

-(id)tableView:(NSTableView *)cocoaTV:objectValueForTableCollumn:(NSTableColumn *)tableCollumn row:(int)row is wrong.. i'm not sure how it compiles, to be honest (unless there was an error copy/pasting it). the method should look like:
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSArray *tvArray = [[NSArray alloc]initWithObjects:#"1",#"2",#"3",#"4",#"5", nil];
NSString *v = [tvArray objectAtIndex:row];
return v;
}

Related

Split View App with Multiple Detail Views

I'm trying to create a split view app with multiple detail view controllers. I'm having trouble with the basic setup/skeleton of the app. First I tried using xcode's Master-Detail Application template. The problem is that the classes from the template look something like this:
MasterViewController.h
MasterViewController.m
DetailViewController.h
DetailViewController.m
But what I want is something like this:
MasterViewController.h
MasterViewController.m
TitleViewController.h
TitleViewController.m
DateViewController.h
DateViewController.m
...
I can't figure out how to get my view controllers to load when user selects a new row.
I also tried using the sample MultipleDetailViews app from Apple, but the sample app has multiple issues for me including the fact that it uses nib files, which I don't want.
Can anyone help? Is there some tutorial about how to set up a split view app with multiple detail view controllers (without nibs)? Thank you!
*response:
Thanks! Could you post the link for the BigNerdRanch one? I couldn't find it. Also, I couldn't follow the Raywenderlich one because it uses an older version of xcode. Following your sample project, I think I'm close, but I'm getting a "terminating with uncaught exeption...". Here's what I did:
Create new project from Master-Detail template.
Add files MyTableViewController.h and MyTableViewController.m.
In MasterViewController.m, set the number of sections to 1 and the number of rows to 2.
In MasterViewController.h add property "MyTableViewController *myTableViewController".
(When user clicks 1st row, detailViewController should show, when he clicks 2nd row, myTableViewController should show.)
In MasterViewController.m, change didSelectRowAtIndexPath to:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int row = [indexPath row];
if( row == 0 ) {
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self.detailViewController];
NSArray *vcs = [NSArray arrayWithObjects:[self navigationController],nav, nil];
[[self splitViewController] setViewControllers:vcs];
}
else {
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self.myTableViewController];
NSArray *vcs = [NSArray arrayWithObjects:[self navigationController],nav, nil];
[[self splitViewController] setViewControllers:vcs];
}
}
The project runs, but when I click on the 2nd row (row == 1) I get the exception.
*update 2
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
int row = [indexPath row];
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
UINavigationController *detailNav = [delegate.splitController.viewControllers objectAtIndex: 1];
NSArray *viewControllers = nil;
switch (row) {
case 0:
viewControllers = [[NSArray alloc] initWithObjects: delegate.dateController, nil];
break;
case 1:
viewControllers = [[NSArray alloc] initWithObjects: delegate.repeatController, nil];
default:
break;
}
[delegate.splitController removeFromParentViewController];
detailNav.viewControllers = viewControllers;
[delegate.window addSubview: delegate.splitController.view];
}
There's actually a great tutorial by the BigNerdRanch on how to use UISplitViewControllers. Honestly, there's no difference between using them to push a different UIViewController as the Detail or Master viewController. They just need a reference to one another and when an event happens, you push a new viewController to one side or the other. I've attached a sample project below for you to reference.
sample project: link
Here's another tutorial on setting one up via Raywenderlich: link
So the following happenins inside a UITableViewController which is the MasterViewController.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 1. get the controllers from the split view [master, detail]
let controllers = split.viewControllers
// 2. get nav controller for the detail + the current storyboard
let navigationController = controllers[controllers.count-1] as!
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// 3. create the appropriate view controller (vc) then just replace the navigation controllers root vc with your new one using 'navigationController.setViewControllers([vc] ...'
switch indexPath.row {
case 1:
let vc = storyboard.instantiateViewController(withIdentifier: "DetailTableViewController") as! DetailTableViewController
navigationController.setViewControllers([vc], animated: false)
case 2:
let vc = storyboard.instantiateViewController(withIdentifier: "DetailCollectionViewController") as! DetailCollectionViewController
navigationController.setViewControllers([vc], animated: false)
default:
let vc = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
navigationController.setViewControllers([vc], animated: false)
break
}
}
You can do the same thing with segues. From the MasterViewController in the storyboard, drag a segue to a navigation controller and set the segue kind as a 'Show Detail (e.g. replace)'. Then from there in you didSelectRowAt you can call performSegue with identifier. Make sure that your segue has an 'Identifier'
Have a look at this video

useLayoutToLayoutNavigationTransitions in iOS7 crash

I am building an iOS7 app and I am trying to make use of the new useLayoutToLayoutNavigationTransitions effect. I have 2 UICollectionViewControllers and when I do the following I get the transition effect
SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondVC.useLayoutToLayoutNavigationTransitions = YES;
[self.navigationController pushViewController:secondVC animated:YES];
this works fine but what I want to do is make an api call and then in the completion block I want to push onto the nav stack like so
[webAPI getDetailsWithParams:params andCompletionBlock:^(id dict) {
//some work on the result
SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondVC.useLayoutToLayoutNavigationTransitions = YES;
[self.navigationController pushViewController:secondVC animated:YES];
} andErrorBlock:^(NSError *error) {
}];
but this crashes every time with the following msg
-[UICollectionView _invalidateLayoutWithContext:]: message sent to deallocated instance 0x17a26400
can anyone tell me what I am doing wrong in this case? How can I get the transition effect when pushing from completion block?
EDIT: by changing it to the following I was able to transition to the second viewcontroller.
MyLayout *layout = [[MyLayout alloc] init];
SecondViewController *expandedVC = [[SecondViewController alloc] initWithCollectionViewLayout:layout];
and I also deleted the nib file that went with the file. nib file just consisted of a collection view and it was the file owners view.
While I can now transition I still do not understand why I could not do the previous nib method with in a block. So I would be grateful if someone could shed some light on it for me.
In order to use UICollectionViewController's useLayoutToLayoutNavigationTransitions to make transitions, the layout of the SecondViewController must be known. However, if you use initWithNibName:bundle: initializer, layout is not internally prepared yet, making your desired transitions impossible. As you mentioned in your edited question, you have to use [UICollectionViewController initWithCollectionViewLayout:] to initialize your second UICollectionViewController. Since your xib file has the same name as your class name, SecondViewController.xib is going to be loaded automatically by UIViewController, superclass of UICollectionViewController.
I think you were making UIKit calls from a thread that wasn't the main thread

NSTableView with images

I have NSTableView with 3 tabs.
Now, I want an image inside every tab, how can i do ?
I must use NSImage with position, so ?
NSImage *theImage;
theImage = [NSImage imageNamed#"myImage.png"];
you can do like this:
in code create one NSMutableArray with property and synthesize
header file:
#property (readwrite, retain) NSMutableArray *imageArray
implementation file:
#synthesize imageArray
do like this wherever you want to add image to array:
NSMutableDictionary *imageDict = [NSMutableDictionary dictionary];
[imageDict setObject:[NSImage imageNamed:#"ABC"] forKey:#"image1"];
[imageDict setObject:[NSImage imageNamed:#"XYZ"] forKey:#"image2"];
[imageDict setObject:[NSImage imageNamed:#"XYZ"] forKey:#"image3"];
[imageArray imageDict];
[self setImageArray:imageArray];
take array controller in XIb, bind arraycontroller to imageArray array, bind table column with the array controller with given key path (here keypath is image1,image2 and image3).
one More thing take image name without extension . drag and drop image cell on tableview cell

UITextField not setting NSString

I have a pickerView in a popOver.
When the user selects a row from the pickerView, it should be stored into a UITextField (subject)
The method i'm having troubles is:
- (void) viewWithPickerController:(ViewWithPickerController*) viewWithPickerController didSelectValue:(NSString*) value
{ NSLog(#"selected value is: %#",value);
subject.text = [NSString stringWithFormat:#"%#",value];
NSLog(#"subject is: %#",subject.text);
}
Where value is the row of the pickerView: it logs the right value but it doesn't set it to the textField subject, which seems to be null.
Where am i wrong?
You need to make sure your "subject" IBOutlet is set.
If you're doing this picker view method before your XIB / view controller is instantiated, that would explain why subject is a nil object.
How else would you be instantiating "subject"?

cellForRowAtIndexPath: returns nil for a generic prototype cell

This is driving me nuts!
I have a generic UITableViewController class with a generic prototype cell, with an identifier "myCell". Developing under ARC, iOS5 and using Storyboard, I am using the following method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myCell"];
cell.textLabel.text = #"Title";
return cell;
}
Everything is hooked up in the storyboard, file owner, etc. I have several other UITableViewController in my project, using the same concept. All are working.
This particular class of UITableViewController doesn't want to work! keep throwing me the exception:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
Nslog'ed --> [tableView dequeueReusableCellWithIdentifier:#"myCell"] = (null)
Any idea and help is greatly appreciated!
EDIT:
For simplicity:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
Things that I have already done:
1. delete and create and set up the UITableViewController in storyboard!
2. Restarted Xcode
3. Restarted simulator
4. Restarted computer!!!!
5. Deleted and built the app over and over!
None worked.
By the way, I tried
if (cell == nil)
cell = [UITableViewCell alloc] initWithStyle....
.. and it will solve the problem, but again... this is ARC, storyboard, ... I am not supposed to do that. Xcode is supposed to take care of it!
You have to create your cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"myCell"] autorelease];
}
cell.textLabel.text = #"Title";
return cell;
}
Eventually I found the bug. I can't say I found the root cause, but investigating line by line, this is what I found and worked for me.
I have some objects in my UITableViewController that need to be alloc/init'ed before the view gets loaded, so that when callers can set them to pre-determind values. Since viewDidLoad is too late, I put them in initWithCoder method.
Commeting out and re-writing the initwithCoder method solved the problem. It seemed to me, that initWithCoder method was initing the UITableViewController as some different!
I had the same problem and just managed to overcome it.
If you are creating cells data dynamically you have to do the following things:
Select the table view.
Then go to the object inspector to the third tab from the right.
The first option is named: "Content". Change the selection from "Static Cells" to "Dynamic Prototypes".
Make sure you set the identifier of the cell properly and it is the same name you use on the "dequeueResableCellWithIdentifier"
It worked for me. I stopped getting "nil" cell values and everything seems to work properly.
if the "IF" statement is falling to true (cell == nil) for the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"myCell"] autorelease];
}
cell.textLabel.text = #"Title";
return cell;
}
then the name #"myCell" was either misspelled (Doesn't match) or is missing from the storyboard identifier field for the cell.

Resources