Setting dataSource for NSTableView across scenes in a storyboard - cocoa

I am using XCode 6 on OS X 10.10 and have a storyboard containing a window with a split view controller, as shown in the following image.
The split view controller (highlighted in the image) is an instance of MyViewController, which has the following code:
MyViewController.h
#import <Cocoa/Cocoa.h>
#interface MyViewController : NSSplitViewController <NSTableViewDataSource>
#end
MyViewController.m
#import "MyViewController.h"
#implementation MyViewController
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return 7;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
return [NSString stringWithFormat:#"%ld", (long)row];
}
#end
I would like to make the view controller the dataSource of the NSTableView in my storyboard, however I am unable to connect them. Is there a reason for this?

In your NSSplitViewController-subclass viewDidLoad-method set the data source programmatically. You need to implement the child view controller class as well (with the tableView outlet connected to the control).
MySplitViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
for (NSSplitViewItem *item in self.splitViewItems)
{
NSViewController *controller = item.viewController;
if ([controller isKindOfClass:[MyChildController class]])
{
MyChildController *myController = (MyChildController *)controller;
myController.tableView.dataSource = self;
[myController.tableView reloadData];
}
}
}
But to tell you the truth I don't like this approach. It's more better when the table view's data source methods are in the native view controller class.
Another way to do that. MyChildController.h file:
#class MyChildViewController;
#protocol MyChildControllerDelegate <NSObject>
- (void)childController:(MyChildViewController *)controller didSelectRowAtIndex:(NSUInteger)index;
#end
#interface MyChildViewController : NSViewController <NSTableViewDataSource, NSTableViewDelegate>
#property (nonatomic, weak) id<MyChildControllerDelegate> delegate;
#property (nonatomic, retain) NSArray *items;
#property (nonatomic, weak) IBOutlet NSTableView *tableView;
#end
Don't forget to implement all the table view dataSource and delegate methods you need. MySplitViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
for (NSSplitViewItem *item in self.splitViewItems)
{
NSViewController *controller = item.viewController;
if ([controller isKindOfClass:[MyChildController class]])
{
MyChildController *myController = (MyChildController *)controller;
myController.delegate = self;
[myController setItems:_items];
}
}
}

Related

How to do searching in NSTableView with NSSearchField?

I have implemented a application in which I use NSTableview with the help of its data source and delegates I have not used NSArrayController nor I want to use it. My question is how can I bind NSSearchField with my NSTableView in this situation? I had seen a lot of answer using NSArrayController.
I do not want to convert implementation to NSArrayController as things are working good with NSMutableArray.
TableView is a display control and is not for filtering.
You should add 2 NSArray properties;
1) #property(nonatomic, strong) NSArray *allItems;
2) #property(nonatomic, strong) NSArray *filteredItems;
#import "ViewController.h"
#interface ViewController()<NSSearchFieldDelegate, NSTableViewDelegate, NSTableViewDataSource>
// Your NSSearchField
#property (weak) IBOutlet NSSearchField *searchField;
// Your NSTableView
#property (weak) IBOutlet NSTableView *tableView;
// In this array you will store all items
#property(nonatomic, strong) NSArray *allItems;
// In this array you will store only filtered items
#property(nonatomic, strong) NSArray *filteredItems;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.searchField.delegate = self;// You can set delegate from XIB/Storyboard
self.tableView.delegate = self;// You can set delegate from XIB/Storyboard
self.tableView.dataSource = self;// You can set dataSource from XIB/Storyboard
self.allItems = #[#"Test1", #"Demo filter", #"Test 2", #"Abracadabra"];
[self applyFilterWithString:#""];
}
- (void)controlTextDidChange:(NSNotification *)obj{
if (obj.object == self.searchField) {
[self applyFilterWithString:self.searchField.stringValue];
}
}
-(void)applyFilterWithString:(NSString*)filter {
if (filter.length>0) {
NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:#"self CONTAINS[cd] %#", filter];
self.filteredItems = [self.allItems filteredArrayUsingPredicate:filterPredicate];
}
else {
self.filteredItems = self.allItems.copy;
}
[self.tableView reloadData];
}
#pragma mark - ***** NSTableViewDataSource, NSTableViewDelegate *****
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return self.filteredItems.count;
}
// for the "Cell Based" TableView
- (nullable id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row {
NSString *item = self.filteredItems[row];
return item;
}
#end

iOS 7: Cannot connect to IBOutlets, probably because I'm using UITableViewController

I cannot create any IBOutlets. I'm using a tableviewcontroller instead of a viewcontroller.
When I click on TableViewController, the class is UITableViewController and I can't change that.
Here's my code for ViewController.h:
// ViewController.h
// Tips4
//
// Created by Meghan on 1/20/14.
// Copyright (c) 2014 Meghan. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (strong, nonatomic) IBOutlet UILabel *sliderDisplay;
#property (strong, nonatomic) IBOutlet UITextField *tempText;
#property (strong, nonatomic) IBOutlet UILabel *billTotal;
#property (nonatomic, strong) IBOutlet UISlider *slider;
- (IBAction)sliderValueChanged:(id)sender;
#property (nonatomic) float answer;
#property (nonatomic) float answerTwo;
#end
Here's my ViewController.m:
// ViewController.m
// Tips4
//
// Created by Meghan on 1/20/14.
// Copyright (c) 2014 Meghan. All rights reserved.
//
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *tipsTableIdentifier = #"TipsTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tipsTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:tipsTableIdentifier];
}
return cell;
}
- (IBAction)sliderValueChanged:(id)sender
{
float theText = [_tempText.text floatValue];
_answer = (_slider.value * theText) / 100;
_answerTwo = _answer + theText;
_sliderDisplay.text = [NSString stringWithFormat:#"%1.2f", _answer];
_billTotal.text = [NSString stringWithFormat:#"%1.2f", _answerTwo];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_tempText.keyboardType = UIKeyboardTypeDecimalPad;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
If you’re using a TableViewController, your class must inherit from a UITableViewController. Thats when it will show up in the Identity Inspector, which is where you change your class from UIViewController to your class. After that you should be able to connect IBOutlets.
To do that, just replace your current line
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
with
#interface ViewController : UITableViewController
Now, you will need to add the init method that calls the super, to the .m file.
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
I hope this will do it.
Also, sometimes, changes in the code don’t show up in the storyboard Identity Inspector. In that case, you can just quit the Xcode window and open the project again. That does it.
Alternatively, if you use a ViewController, your class can inherit from a UIViewController. Then you add a TableView to your View and add a UITableView instance to the controller file (create an IBOutlet). In this case, your .h file needs to add the UITableViewDelegate and UITableViewDataSource to populate the table and your .m file needs to implement the required methods (Xcode will warn you about this).

Cocoa : Load NSViewController with nib

I have tried many possibilities I found on this site and read some explanations
from apple developer page, but seems like i couldn't resolve my problem about
loading NSViewController with/form NIB.
Files on Xcode Project look a bit like this :
SecondViewController.h
SecondViewController.m
SecondViewController.xib
AppDelegate.h
AppDelegate.m
MainMenu.xib
The main problem is how could i create the SecondViewController programatically with
the initial nib on SecondViewController.xib
Custom class of FileOwner on MainMenu.xib is NSApplication
Custom class of FileOwner on SecondViewController.xib is SecondViewController
There are some panels and window in MainMenu.xb (about window and preference panel)
This application has no main window (using notification icon on status bar)
SecondViewController.h
#interface SecondViewController : NSViewController {
BOOL fullScreenMode;
NSImageView *fullScreenbg;
NSImageView *imageView1;
NSImageView *imageView2;
NSPanel *imageWindow;
}
#property (assign) IBOutlet NSImageView *fullScreenbg;
#property (assign) IBOutlet NSImageView *imageView1;
#property (assign) IBOutlet NSImageView *imageView2;
#property (assign) IBOutlet NSPanel *imageWindow;
SecondViewController.m
#implementation SecondViewController {
NSImageView *nv1;
NSImageView *nv2;
NSSize curImgSize;
}
#synthesize fullScreenbg;
#synthesize imageView1;
#synthesize imageView2;
#synthesize imageWindow;
......
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
fullScreenMode = YES;
}
return self;
}
AppDelegate.h
#interface AppDelegate : NSObject <NSApplicationDelegate,NSWindowDelegate> {
NSPanel *aboutWindow;
IBOutlet NSMenu *myStatusMenu;
IBOutlet NSMenuItem *toggleFullScreen;
}
AppDelegate.m
#implementation AppDelegate {
SecondViewController *controller;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
controller = [[SecondViewController alloc]
initWithNibName:#"SecondViewController"
bundle:nil];
//Not work (fullscreenbg, imageView1, imageView2,imageWindow = nil)
//controller = [[SecondViewController alloc] init]; ?? <-- didn't work either
}
Even if using initWithNibName or just init, all the IBOutlet properties seems to be nil
on debug.
i've tried other solustions like "NSBundle loadNibNamed" or using loadView but it didn't work (warning message : "NSObject my not respond to loadView").
The main purpose of the secondViewController is to display notification message including
graphics and web element.
I hope someone could give me a best suggestion. Thanks.
This is normal behavior. IBOutlets are not connected in the constructor.
You can override viewDidLoad, call super and then do any initialization.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
//added this line :
if (nibBundleOrNil!=nil || ![nibBundleOrNil isEqualtoString:#""]) {
[NSBundle loadNibNamed:#"SecondViewController" owner:self];
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
fullScreenMode = YES;
}
return self;
}

Semantic issue: Property 'text' not found on object of type 'UISlider'

When I try to run it though, I get an error which says "Semantic issue: Property 'text' not found on object of type 'UISlider'"
What's wrong here?
Here is the code from the header file:
#import <UIKit/UIKit.h>
#interface BIDViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *nameField;
#property (strong, nonatomic) IBOutlet UITextField *numberField;
#property (strong, nonatomic) IBOutlet UISlider *sliderLabel;
- (IBAction)textFieldDoneEditing:(id)sender;
- (IBAction)backgroundTap:(id)sender;
- (IBAction)sliderChanged:(id)sender;
#end
Implementation file:
#import "BIDViewController.h"
#implementation BIDViewController
#synthesize sliderLabel;
#synthesize nameField;
#synthesize numberField;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[self setNameField:nil];
[self setNumberField:nil];
[self setSliderLabel:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (IBAction)textFieldDoneEditing:(id)sender
{
[sender resignFirstResponder];
}
- (IBAction)backgroundTap:(id)sender
{
[nameField resignFirstResponder];
[numberField resignFirstResponder];
}
- (IBAction)sliderChanged:(id)sender
{
UISlider *slider = (UISlider *)sender;
int progressAsInt = (int)roundf(slider.value);
sliderLabel.text = [NSString stringWithFormat:#"%d",progressAsInt];
}
#end
I suspect your sliderLabel has been declared as an object of class UISlider, when it should be UILabel. Can you verify the sliderLabel's #property declaration in your header file?
I have the same message on NSManagedObject. My solution is set "Always Search User Paths" in "Search Paths" of target's Build Settings to "No"
Hope that could help too.

NSTableView: no text in new rows

I wrote a method to add new rows to NSTableView. It adds new rows, but there're empty, without text.
Here's code:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSButton *addDreamButton;
IBOutlet NSButton *removeDreamButton;
IBOutlet NSTextField *dreamField;
IBOutlet NSTableView *dreamTable;
IBOutlet NSMutableArray *dreamlist;
IBOutlet NSTextField *testf;
}
- (IBAction)addDream:(id)sender;
- (IBAction)deleteDream:(id)sender;
#property (assign) IBOutlet NSWindow *window;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
-(int) numberOfRowsInTableView: (NSTableView *) tableView
{
return [dreamlist count];
}
-(void) dreamTable: (NSTableView *) tableview
setObjectValue:(id)anObject
objectValueForTableColumn: (NSTableColumn *) tablecolumn
row:(int) rowIndex
{
[dreamlist replaceObjectAtIndex:rowIndex withObject:anObject];
}
- (IBAction)addDream:(id)sender
{
NSString* thedream = dreamField.stringValue;
[dreamlist addObject: thedream];
[dreamTable reloadData];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
dreamlist = [[NSMutableArray alloc] init];
}
#end
AppDelegate is bound as datasource with TableView.
So when I press "add" button empty rows appears in table (textfield where I get text is not empty; I debugged and saw that MutableArray contains right strings).
Your table view data source needs to implement this method:
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
return [dreamlist objectAtIndex:rowIndex];
}

Resources