Can an array controller take inputs before "add" is pressed? - cocoa

In Cococa,
Can I link input fields (say a textField) to my array controller so that a new object is added with attributes already populated?
Thanks

Try the below code as it is done for one textfield:-
Header File
#import <Cocoa/Cocoa.h>
#interface testWindowController : NSWindowController
{
IBOutlet NSArrayController *arrayController;
NSMutableArray *array;
}
#property(readwrite,retain)NSMutableArray *array;
#end
Implememntation file:-
#import "testWindowController.h"
#interface testWindowController ()
#end
#implementation testWindowController
#synthesize array;
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (void)windowDidLoad
{
NSString *str=#"testValue";
self.array=[[NSMutableArray alloc]init];
NSMutableDictionary *dict=[NSMutableDictionary dictionary];
[dict setObject:str forKey:#"valueText"];
[self.array addObject:dict];
[self setArray:self.array];
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
-(NSString *)windowNibName
{
return #"testWindowController";
}
#end
XIB file:-
Follow below steps:-
1) In this file take one arraycontroller and textfield and then connect your arraycontroller to FileOwner.
2) Then in Binding Inspector bind arraycontroller to FileOwners and mention model key path as array.
3) Now bind your textfield to arraycontroller and model key path as "key:" whose name is valueText.

Related

NSContainerView with multiple child view controllers in Xcode 6

I'm trying to link two different view controllers in an Xcode 6 storyboard with a NSContainerView so that they can be switched conditionally. Unfortunately this tutorial here isn't of any help since things seem to have changed since in Xcode.
So I have two different view controllers and one of them is loaded into the container view by default but I want to be able to load the second view controller into the container view programmatically. Xcode 6 only allows to create embed Segues when I drag from one to the other so that's not much of a help.
Can somebody tell me how this is achieved with Xcode 6?
First, here's a sample GitHub project of the solution: click. I wasn't sure whether you wanted to swap the views or simply push the 2nd view onto a proverbial stack, so I went with a push/pop scheme. If you want to swap the views instead, you should be able to do that fairly easily by just skipping the stack storage.
Essentially, we have our "host" NSViewController that holds a Container View (CV) inside of it. This host doesn't actually manually manage the view controller that the CV is showing at the moment. The way this is done is through, well, a sort of nested view controller that then manages all the other view controllers that you're going to show/hide/push/pop/swap/etc. (Note: you might be able to remove the layering a bit, but in iOS terms, I'm treating the 'Sub View Controller Manager' in the storyboard screenshot sort of like a UINavigationController).
We also take advantage of some custom segues/segue animators in order to be able to do more work in the storyboard.
You just have to tell the content view manager view controller to manipulate its subviews in such a way that the old views that you want to pop "back" to are retained (in this case, using an NSMutableArray) and such that the new views have the right frame or have their constraints set up properly.
Here is a screenshot of the storyboard: Each segue you see on the storyboard of a custom type (looks like this -> { } ) is of type SegueBetweenEmbedded in the sample project. Buttons that push perform a segue, and buttons labeled 'Pop' perform dismissController: on the NSViewController (so that was done in the storyboard).
Here's some code (and there's a lot of it, so I suggest looking at the sample project instead):
ViewController.h
#import <Cocoa/Cocoa.h>
#import "ContentManagerViewController.h"
#class ContentManagerViewController;
#protocol ContentManagerViewControllerHolder <NSObject>
-(ContentManagerViewController*)retreiveContentManagerController;
#end
#interface ViewController : NSViewController <ContentManagerViewControllerHolder>
#end
ViewController.m
#import "ViewController.h"
#import "ContentManagerViewController.h"
#import "BackForwardViewController.h"
#interface ViewController ()
#property ContentManagerViewController *vcController;
-(IBAction)pushViewController:(id)sender;
-(IBAction)popViewController:(id)sender;
-(IBAction)popToRootViewController:(id)sender;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
if ([[segue destinationController] class] == [ContentManagerViewController class]) {
self.vcController = segue.destinationController;
}
}
-(ContentManagerViewController*)retreiveContentManagerController {
return self.vcController;
}
-(IBAction)pushViewController:(id)sender {
// note: this works, but then pop is broken via dismissController: since it wasn't done with a segue.
// Better way is to rig up a manual segue and execute the segue.
//BackForwardViewController *viewController = [[NSStoryboard storyboardWithName:#"Main" bundle:nil] instantiateControllerWithIdentifier:#"BackForwardStoryboardID"];
//[self.vcController push:viewController];
[self performSegueWithIdentifier:#"CustomSegueToBackForward" sender:self];
}
-(IBAction)popViewController:(id)sender {
[self.vcController pop];
}
-(IBAction)popToRootViewController:(id)sender {
[self.vcController popToRoot];
}
#end
SegueBetweenEmbedded.h
#import <Cocoa/Cocoa.h>
#interface SegueBetweenEmbedded : NSStoryboardSegue
#end
SegueBetweenEmbedded.m (sorry not sorry for the nested class)
#import "SegueBetweenEmbedded.h"
#import "ContentManagerViewController.h"
#import "ViewController.h"
#interface SegueAnimator : NSObject <NSViewControllerPresentationAnimator>
- (void)animatePresentationOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController;
- (void)animateDismissalOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController;
#end
#implementation SegueAnimator
- (void)animatePresentationOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
NSViewController *parent = [fromViewController parentViewController];
if (parent && [parent class] == [ContentManagerViewController class]) {
ContentManagerViewController *manager = (ContentManagerViewController*)parent;
[manager push:viewController];
}
else if ([fromViewController conformsToProtocol:#protocol(ContentManagerViewControllerHolder)]) {
id<ContentManagerViewControllerHolder> holder = (id<ContentManagerViewControllerHolder>)fromViewController;
[[holder retreiveContentManagerController] push:viewController];
}
}
- (void)animateDismissalOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
NSViewController *parent = [viewController parentViewController];
if ([parent class] == [ContentManagerViewController class]) {
ContentManagerViewController *manager = (ContentManagerViewController*)parent;
[manager pop];
}
}
#end
#implementation SegueBetweenEmbedded
- (void)perform {
SegueAnimator *animator = [[SegueAnimator alloc] init];
[self.sourceController presentViewController:self.destinationController
animator:(id<NSViewControllerPresentationAnimator>)animator];
}
#end
ContentManagerViewController.h
#import <Cocoa/Cocoa.h>
#interface ContentManagerViewController : NSViewController
-(void)push:(NSViewController*)viewController;
-(void)pop;
-(void)popToRoot;
#end
ContentManagerViewController.m
#import "ContentManagerViewController.h"
#import "BackForwardViewController.h"
#interface ContentManagerViewController ()
#property (weak) IBOutlet NSView *subViewControllerManager;
#property NSViewController *currentViewController;
#property NSMutableArray<NSViewController*> *viewControllerStack;
#end
#implementation ContentManagerViewController
-(instancetype)init {
self = [super init];
self.viewControllerStack = [NSMutableArray array];
return self;
}
-(instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
self.viewControllerStack = [NSMutableArray array];
return self;
}
-(instancetype)initWithNibName:(NSNibName)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
self.viewControllerStack = [NSMutableArray array];
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)showViewController:(NSViewController*)viewController {
[self addChildViewController:viewController];
viewController.view.frame = self.currentViewController.view.frame;
[self.view addSubview:viewController.view];
self.currentViewController = viewController;
}
-(void)removeCurrentViewControllerFromView {
[self.currentViewController.view removeFromSuperview];
[self.currentViewController removeFromParentViewController];
}
-(void)push:(NSViewController*)viewController {
[self removeCurrentViewControllerFromView];
[self.viewControllerStack addObject:viewController];
[self showViewController:viewController];
}
-(void)pop {
if (self.viewControllerStack.count > 1) {
[self removeCurrentViewControllerFromView];
[self.viewControllerStack removeLastObject];
NSViewController *viewController = [self.viewControllerStack lastObject];
[self showViewController:viewController];
}
}
-(void)popToRoot {
while (self.viewControllerStack.count > 1) {
[self pop];
}
}
-(void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
// this will be called on the initial embed to set up the first view controller
self.currentViewController = segue.destinationController;
[self.viewControllerStack addObject:segue.destinationController];
}
#end
BackForwardViewController.h
#import <Cocoa/Cocoa.h>
#interface BackForwardViewController : NSViewController
#end
BackForwardViewController.m
#import "BackForwardViewController.h"
#interface BackForwardViewController ()
#end
#implementation BackForwardViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
#end

Setting dataSource for NSTableView across scenes in a storyboard

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];
}
}
}

IBOutlet for NSOutlineView doesn't point a valid instance

I created a new Cocoa application project in Xcode then add a NSOutlineView and a NSTextView objects onto window. Those two objects were subclassed as MyOutlineView and MyTextView. After that I made two outlets for them and wrote code like below.
The problem, I found, is application has two different MyOutlineView instances in runtime. Working(valid) outline view instance is not equal to the myOutlineView outlet instance. What am I missing?
//
// AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "MyOutlineView.h"
#import "MyTextView.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet MyOutlineView *myOutlineView;
#property (unsafe_unretained) IBOutlet MyTextView *myTextView;
#end
//
// AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)n
{
NSLog(#"AppDelegate.myOutlineView(INVALID)::%#", _myOutlineView);
NSLog(#"AppDelegate.myTextView::%#", _myTextView);
}
#end
//
// MyOutlineView.h
#import <Cocoa/Cocoa.h>
#interface MyOutlineView : NSOutlineView <NSOutlineViewDataSource>;
#end
//
// MyOutlineView.m
#import "MyOutlineView.h"
#implementation MyOutlineView
- (id)initWithCoder:(NSCoder *)aDecoder
{
// This method is called first.
self = [super initWithCoder:aDecoder];
NSLog(#"MyOutlineView initWithCoder(INVALID)::%#", self);
return self;
}
- (id)initWithFrame:(NSRect)frame
{
// This method is also called but through a different instance with first one.
self = [super initWithFrame:frame];
NSLog(#"MyOutlineView initWithFrame(valid)::%#", self);
return self;
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
NSLog(#"MyOutlineView data source delegate(valid)::%#", self);
return 0;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
return nil;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
return NO;
}
#end
//
// MyTextView.h
#import <Cocoa/Cocoa.h>
#interface MyTextView : NSTextView
#end
//
// MyTextView.m
#import "MyTextView.h"
#implementation MyTextView
- (id)initWithCoder:(NSCoder *)aDecoder
{
// This method is called.
self = [super initWithCoder:aDecoder];
NSLog(#"MyTextView initWithCoder::%#", self);
return self;
}
- (id)initWithFrame:(NSRect)frame
{
// But this method is NOT called at all.
self = [super initWithFrame:frame];
NSLog(#"MyTextView initWithFrame::%#", self);
return self;
}
#end
Output:
MyTextView initWithCoder:: [MyTextView: 0x10013be80]
MyOutlineView initWithCoder(INVALID):: [MyOutlineView: 0x10014bc90]
MyOutlineView initWithFrame(valid):: [MyOutlineView: 0x1001604a0]
MyOutlineView data source delegate(valid)::[MyOutlineView: 0x1001604a0]
AppDelegate.myOutlineView(INVALID):: [MyOutlineView: 0x10014bc90]
AppDelegate.myTextView:: [MyTextView: 0x10013be80]
Because of this, I have to put "AppDelegate.myOutlineView = self;" into MyOutletView's implementation wherever it calls related methods of AppDelegate. It does not seem natural.
Xcode doesn't seem to let you set an outline view's delegate or data source to itself.
So I'm guessing you're doing something like this:
Which is to say: instantiating a second copy of your custom outline view class.
Here's the output from this setup:
2012-09-26 14:11:34.511 testproj[30255:403] -[MyOutlineView initWithCoder:]
2012-09-26 14:11:34.531 testproj[30255:403] -[MyOutlineView initWithFrame:]
By removing the extra (highlighted) instance of My Outline View, the initWithFrame: line goes away.
To make the outline view its own delegate, do this instead:
- (void) awakeFromNib {
self.delegate = self;
}
That said, the point of the Delegation pattern is avoiding the need to subclass. If you do need an outline view subclass, try overriding the NSOutlineView / NSTableView methods directly, instead of using the delegate protocol.
I can't reproduce your problem. I dropped all your code that you posted into a test app, and I only get one instantiation of each object. Neither of the initWithFrame methods are getting called when I try it. My output is:
2012-09-26 09:00:38.945 TextViewDoubleInstantiationProblem[451:303] MyTextView initWithCoder::<MyTextView: 0x100123990>
Frame = {{0.00, 0.00}, {381.00, 182.00}}, Bounds = {{0.00, 0.00}, {381.00, 182.00}}
Horizontally resizable: NO, Vertically resizable: YES
MinSize = {381.00, 182.00}, MaxSize = {463.00, 10000000.00}
2012-09-26 09:00:38.953 TextViewDoubleInstantiationProblem[451:303] MyOutlineView initWithCoder(INVALID)::<MyOutlineView: 0x101a1cb90>
2012-09-26 09:00:39.005 TextViewDoubleInstantiationProblem[451:303] AppDelegate.myOutlineView(INVALID)::<MyOutlineView: 0x101a1cb90>
2012-09-26 09:00:39.005 TextViewDoubleInstantiationProblem[451:303] AppDelegate.myTextView::<MyTextView: 0x100123990>
Frame = {{0.00, 0.00}, {381.00, 182.00}}, Bounds = {{0.00, 0.00}, {381.00, 182.00}}
Horizontally resizable: NO, Vertically resizable: YES
MinSize = {381.00, 182.00}, MaxSize = {463.00, 10000000.00}
Do you have any other code in your app that you're not showing?
The calls to initWithCoder: come from loading objects that are defined in a nib file. I assume that's what you want to have happen since you mention creating outlets. In that case, they call to initWithFrame: strikes me as more likely to be "invalid" than the coder one.
I'd set a breakpoint in initWithFrame: and trace where that call is coming from in order to identify the extra allocation.

hide an object from another class

I'm trying to hide an object in my viewController, with code executed from a custom class, but the object is nil.
FirstViewController.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController {
IBOutlet UILabel *testLabel;
}
#property (nonatomic, retain) IBOutlet UILabel *testLabel;
- (void) hideLabel;
FirstViewController.m
I synthesize testLabel and I have a function to hide it. If I call the function from viewDidAppear it works, but I want to call it from my other class. When called from the other class, testLabel is nil
#import "FirstViewController.h"
#import "OtherClass.h"
#implementation FirstViewController
#synthesize testLabel;
- (void) hideLabel {
self.testLabel.hidden=YES;
NSLog(#"nil %d",(testLabel==nil)); //here I get nil 1 when called from OtherClass
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
OtherClass *otherClass = [[OtherClass alloc] init];
[otherClass hideThem];
//[self hideLabel]; //this works, it gets hidden
}
OtherClass.h
#class FirstViewController;
#import <Foundation/Foundation.h>
#interface OtherClass : NSObject {
FirstViewController *firstViewController;
}
#property (nonatomic, retain) FirstViewController *firstViewController;
-(void)hideThem;
#end
OtherClass.m
calls the hideLabel function in FirstViewController. In my original project, (this is an example obviously, but the original project is at work) I download some data here and I want to hide my loading label and indicator when download is done
#import "OtherClass.h"
#import "FirstViewController.h"
#implementation OtherClass
#synthesize firstViewController;
-(void)hideThem {
firstViewController = [[FirstViewController alloc] init];
//[firstViewController.testLabel setHidden:YES]; //it doesn't work either
[firstViewController hideLabel];
}
Any ideas?
Your UILabel is nil because you just initialized your controller but didn't load it's view. Your controller`s IBoutlets are instantiated from the xib or storyboard automatically when you ask access to the bound view for the first time, so in order to access them you first have to load its view by some means.
EDIT (after OP comments):
Since your FirstViewController is already initialized and your OtherClass is instantiated by that controller, you could just hold a reference to it and not try to initialize a new one.
So try something like this:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
OtherClass *otherClass = [[OtherClass alloc] init];
otherClass.firstViewController = self;
[otherClass hideThem];
}
In your OtherClass.m:
-(void)hideThem {
[self.firstViewController hideLabel];
}

Cocoa Drag and Drop delegates not working the way I would expect

I made a very simple class that subclasses NSTextField so I could have more control over the drag and drop behavior, but I'm running into confusion. According to what I think I understand in the apple.developer docs on Dragging destinations, I should be able to get the delegates to fire when I enter or exit the bounds of the text field, but what I get instead is that the delegates don't fire until I have dropped something into the textField and then I try to drag it out.
I have a MyController class which is an NSObject and I have a CustomFields class of type NSTextField; There is nothing in the controller class.
Here is the code:
// CustomFields.h
// Drags
//
#import <AppKit/AppKit.h>
#import <Foundation/foundation.h>
#interface CustomFields : NSTextField{
NSString *tempStorage_;
unsigned long last_;
}
#property(readwrite, retain) NSString *tempStorage;
#end
//
// CustomFields.m
// Drags
//
#import "CustomFields.h"
#implementation CustomFields
#synthesize tempStorage = tempStorage_;
- (id)init{
if (self) {
self = [super init];
}
return self;
}
-(void)dealloc{
[self.tempStorage release ];
[super release];
}
-(void)awakeFromNib{
NSLog(#"Awake from nib called");
self.tempStorage = #"";
self->last_ = 0;
[self setBackgroundColor:[NSColor lightGrayColor]];
[self registerForDraggedTypes:[NSArray arrayWithObjects: NSPasteboardTypeString , NSPasteboardTypeString, nil]];
[super awakeFromNib];
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
NSLog(#"Inside dragging entered");
return NSDragOperationCopy;
}
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
{
NSLog(#" draggingUpdated called");
return NSDragOperationCopy;
}
- (void)draggingExited:(id <NSDraggingInfo>)sender
{
NSLog(#"Inside dragging exited");
}
#end
//
// MyController.h
// Drags
//
#import <Foundation/Foundation.h>
#class CustomFields;
#interface MyController : NSObject{
IBOutlet NSButton *myButton_;
IBOutlet CustomFields *field1_;
IBOutlet CustomFields *field2_;
}
#end
//
// MyController.m
// Drags
//
#import "MyController.h"
#import "CustomFields.h"
#implementation MyController
#end
I made sure I assigned the correct class to the fields(CustomFields) in IB and connected them. As you can see, for the moment I would be happy to get log statements at the proper time. I also tried changing the MyController class to NSViewController and NSView so I could try registerForDraggedTypes there. That did'nt help anything and I don't think I should expect it to. I think the behaviors I want should be confined to the CustomFields Class which is one of the reasons the MyController class can be empty for the moment.
So any help is much apprecieted.

Resources