This is a follow-up on the previous question.
Sorry. I could not figure out how to add code or edit something written over 5 minues ago.
A brief summary. I am trying to display a customized/derived TableView over a regular View. I am not using IB, but doing everything from the code. The goal here is to build the application, but also to learn Cocoa/OSX programming. This is my first OSX coding attempt.
NSView atop of which I would like to display my custom TableView is being displayed fine. Please excuse the NSLog garbage. It helps me to learn about the app lifecycle.
Header:
#import <Cocoa/Cocoa.h>
#import "MSNavigationTableView.h"
#interface MSNavigationPanelView : NSView
#property (strong, nonatomic) IBOutlet MSNavigationTableView *myNavigationTable;
#end
code:
#import "MSNavigationPanelView.h"
#implementation MSNavigationPanelView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
NSLog(#"Initializing Navigation Panel");
}
self.myNavigationTable = [[MSNavigationTableView alloc] initWithFrame:self.frame];
[self.myNavigationTable setDataSource:self.myNavigationTable];
[self.myNavigationTable setDelegate:self.myNavigationTable];
[self addSubview:self.myNavigationTable];
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
NSLog(#"Drawing navigation view!");
}
#end
Now the NSTableView derived class.
Header:
#import <Cocoa/Cocoa.h>
#interface MSNavigationTableView : NSTableView <NSTableViewDataSource>
#end
NSArray *myNavigationArray;
Source:
#import "MSNavigationTableView.h"
#implementation MSNavigationTableView
+ (void)initialize {
NSLog(#"Called NavigationTableView::initialize!");
myNavigationArray = [NSArray arrayWithObjects:#"Call History" #"Contacts", #"Messages", #"Voicemail", nil];
}
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
- (NSInteger)numberOfRowsInTableView: (NSTableView *) aTableView
{
return [myNavigationArray count];
}
- (id)tableView: (NSTableView*) aTableView objectValueForTableColumn: (NSTableColumn *)aTableColum row: (NSInteger)rowIndex
{
NSLog([myNavigationArray objectAtIndex:rowIndex]);
return [myNavigationArray objectAtIndex:rowIndex];
}
#end
Thank you. I am sure that I am doing something stupid, and/or perhaps not doing something necessary. I have tried to figure this out for a couple of hours. No ideas so far.
You really need to use Interface Builder to make a table.
I would never try to programmatically initialized a table... to many things to configure.
NSTableView needs to have NSTableColumns, NSTableColumns need to have NSCell's, etc. etc.
NSTableView needs to be embedded in an NSScrollView.
I figured out what needs to be done.
First, array initialization has to be moved from +(void)initialize to another method. For me - (id)initWithFrame works fine.
Second, while this was not clear for me, overwriting NSTableViewDataSource is not enough.
One has to create NSTableColumn(s) then add the column(s) to the table using addTableColumn method of NSTableView class. Once that is done, we proceed with setDataSource and so on.
Related
I have successfully implemented a file-drop functionality in my app. The Application window has a few NSTabView objects where dropping on them does not work. Anywhere else in the window the file-drop works fine.
I have tried to make the app delegate a delegate for the NSTabView, but this did not help.
Anyone have a setup for the NSTabView not to filter out the drop-actions so the whole window can be transparent to the file-drop actions ?
For a more generic solution than olekeh's I made it IB friendly so you can hook it up to any object that complies with the NSDraggingDestination protocol.
#import <Cocoa/Cocoa.h>
#interface DropFilesView : NSView
#property (nullable, assign) IBOutlet id<NSDraggingDestination> dropDelegate;
#end
#implementation DropFilesView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
}
-(void) awakeFromNib {
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSFilenamesPboardType,
(NSString *)kPasteboardTypeFileURLPromise,kUTTypeData, NSURLPboardType, nil]]; //kUTTypeData
[super awakeFromNib];
}
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender{
return [self.dropDelegate draggingEntered:sender];
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender {
return [self.dropDelegate performDragOperation:sender];
}
#end
I found the solution to this !! - I am posting it here for others who might need.
The NSTabView object has for each of its tabs an NSTabViwItem.
Under each of those, there is a regular NSView - that I subclassed with the following code: - The code assumes that you already have "draggingEntered" and "performDragOperation" in your AppDelegate as this class just forwards these messages to the app delegate. You will also need to put the declarations for those methods in you AppDelegate.h
// DropFilesView.h
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#interface DropFilesView : NSView
#end
and the implementation:
// DropFilesView.m
#import "DropFilesView.h"
#implementation DropFilesView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
}
-(void) awakeFromNib {
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSFilenamesPboardType,
(NSString *)kPasteboardTypeFileURLPromise,kUTTypeData, NSURLPboardType, nil]]; //kUTTypeData
[super awakeFromNib];
}
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
AppDelegate* del = [AppDelegate sharedAppDelegate];
return [del draggingEntered:sender];
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender {
AppDelegate* del = [AppDelegate sharedAppDelegate];
return [del performDragOperation:sender];
}
#end
In Interfacebuilder, I set the new class for all the NSView objects covering areas where drop does not work, to this new one.
A similar approach can be used for NSImageView and the WebView classes. However, for the last one, do not use [super awakeFromNib] to prevent the default drag-and drop handling for the web view object.
My approach to this may be all wrong so I appreciate your patience.
I have a button in my main XIB file linked to this method in my document.m file:
- (IBAction)showTagModal:(id)sender {
if (!_FileTagWindowController){
_FileTagWindowController = [[FileTagWindowController alloc]init];
}
[_FileTagWindowController showWindow:self];
}
_FileTagWindowController is declared as a property in document.h and using breakpoints when the method is called, as far as I can tell is initializing properly, however _windowNibName and _window remains nil.
FileTagWindowController.h looks like this.
#import <Cocoa/Cocoa.h>
#interface FileTagWindowController : NSWindowController{
}
#property (strong) IBOutlet NSArrayController *tagsArray;
- (IBAction)saveContext:(id)sender;
#end
FileTagWindowController.m looks like this:
#import "FileTagWindowController.h"
#interface FileTagWindowController ()
#end
#implementation FileTagWindowController
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
NSLog(#"Window Did Load!");
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
- (IBAction)saveContext:(id)sender {
}
#end
in my FileTagWindowController.xib I have File Owner set to FileTagWindowController as the custom class. I have the File Owner's "window" outlet linked to the window (NSPanel). That's all that should be required correct? The NSLOG statement in WindowDidLoad never gets called. I tried using [super initWithWindowNibName] in FileTagWindowController.m but that crashes not only the app, but Xcode as well with an endless initialization loop. Am I missing something obvious here?
Thanks all so much.
Try something like the following.
// document.h
#import "FileTagWindowController.h"
#property (strong) filetagWindowController *FileTagWindowController;
// document.m
#synthesize filetagWindowController;
- (IBAction)showTagModal:(id)sender {
if (self.filetagWindowController == nil) {
self.filetagWindowController = [[FileTagWindowController alloc] initWithWindowNibName:#"FileTagWindowController"];
}
[filetagWindowController showWindow:self];
[[filetagWindowController window] setReleasedWhenClosed:NO];
[NSApp runModalForWindow:filetagWindowController.window];
filetagWindowController = nil;
}
You may also want to call NSWindowWillCloseNotification to observe its state and see if filetagWindowController is closed.
I will be very short because of this simple issue:
(This is a document based app, and i will provide just the code I have the issue with)
.h :
#import <Cocoa/Cocoa.h>
#interface Document : NSDocument {
IBOutlet NSTextField *someText;
}
.m :
- (id)init
{
self = [super init];
if (self) {
//Some initialization..
[someText setStringValue:#"Downloading data..."]; //This is not updating !
//Some others..
}
return self;
}
That label is not updating, but why ?? This seem to be a very stupid problem but I can't figure out what I'm doing wrong..
Outlets will not yet be connected when the init method runs. You'll want to move code that depends on nib objects existing to awakeFromNib.
I've written some code where I bring up a new view (from my main view controller); then it calls the main controller when it is closed, like so -
-(void)showMyNewView {
MyNewViewController *myNewViewController = [[MyNewViewController alloc] initWithNibName:#"MyNewViewController" delegate:self];
[self.view addSubview:myNewViewController.view];
}
and then when the new one closes, it calls -
-(void)myNewViewControllerDidFinish:(MyNewViewController *)myNewViewController {
[myNewViewController.view removeFromSuperview];
[myNewViewController release];
}
Now this works fine, and there are no leaks, but the compiler moans with warnings about "Potential leak of an object allocated on line x and stored into myNewViewController".
I've been looking at Apple's presentModalViewController:animated: code, which also doesn't release the new modal view controller in the method which creates it, it seems to release it with a dismissModalViewControllerAnimated: call when the delegate's viewControllerDidFinish: method is called. Is there something I'm missing here? Using the presentModalViewController code doesn't generate any warnings. Many thanks for any help.
I think I've figured it out now, and I've written a small bit of code which gives me my own version of "presentModalViewController:animated:" with all the control I want. I'd be grateful to hear what more seasoned coders make of this (it's probably really straight forward but I've not been doing this for very long...), and if there are any problems with the code, etc -
Interface:
#import <UIKit/UIKit.h>
enum {
MyViewLoaderTransitionTypeNone = 0,
MyViewLoaderTransitionTypeSomeEffect,
MyViewLoaderTransitionTypeSomeOtherEffect
};
typedef NSInteger MyViewLoaderTransitionType;
#interface MyViewLoader : UIViewController {
UIViewController *myLoadedViewController;
}
#property (nonatomic, retain) UIViewController *myLoadedViewController;
-(void)loadView:(UIViewController *)theViewController withTransition:(MyViewLoaderTransitionType)theTransition;
-(void)dismissViewWithTransition:(MyViewLoaderTransitionType)theTransition;
#end
Implementation:
#import "MyViewLoader.h"
#implementation MyViewLoader
#synthesize myLoadedViewController;
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
;
}
return self;
}
-(void)dealloc {
[myLoadedViewController release];
[super dealloc];
}
-(void)loadView:(UIViewController *)theViewController withTransition:(MyViewLoaderTransitionType)theTransition {
[self setLoadedViewController:theViewController];
UIView *theLoadedView = theViewController.view;
[self.view addSubview:theLoadedView];
// do all sorts of transition stuff here
[theViewController viewWillAppear:NO];
}
-(void)dismissViewWithTransition:(MyViewLoaderTransitionType)theTransition {
UIView *theLoadedView = self.loadedViewController.view;
// do all sorts of transition stuff here
[theLoadedView removeFromSuperview];
self.loadedViewController = nil
}
I just use MyViewLoader as the superclass of any view controllers where I need it.
Thanks for any comments / help!
The usual thing to do here is, when you add a subview to a view, release the subview directly after. The parent view becomes responsible for the subview. When removeFromSuperview is called later, that decrements the retain count and the subview is automatically released.
I'm realizing what a newbie I still am with this problem I have. I am trying to present a modal window in a project I am working on and it's not appearing. My solution was then to create an absolute basic project and get it working there first, so I would clearly understand my problem, but I can't get even this working :(
I add a ViewController to the MainWindow at applicationDidFinishLaunching. In this ViewControllers XIB, I have a button. The ViewController has the following header:
#import <UIKit/UIKit.h>
#import "ModalView.h"
#interface ViewBasedViewController : UIViewController {
ModalView *modalView;
}
- (IBAction)dooooIt :(id)sender;
#property (nonatomic, retain, readonly) ModalView *modalView;
#end
And methods:
#import "ViewBasedViewController.h"
#implementation ViewBasedViewController
#synthesize modalView;
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
}
- (void)dealloc {
[super dealloc];
[modalView release];
}
- (ModalView *)modalView {
if (modalView == nil) {
modalView = [[ModalView alloc] initWithNibName:#"ModalView" bundle:nil];
}
return modalView;
}
- (IBAction)dooooIt :(id)sender {
[self.navigationController presentModalViewController:modalView animated:YES];
}
#end
I'm obviously missing something very simple and I believe it's between my ears at this stage :)
Does anyone want to put a poor fella out of his misery?
Many Thanks
Chris
Have you connected the button to the IBAction? Control-drag in Interface Builder from your button to the "File's Owner" icon in your XIB file, and select the "dooooIt" method there. Recompile and your code should execute as expected.
For those that may come across this problem and were as baffled as I was, I fell over the solution. There was two problems in the dooooIt method:
- (IBAction)dooooIt :(id)sender {
[self presentModalViewController:self.modalView animated:YES];
}
I should have included 'self' when referring to the modalView property (otherwise it's nil) and I shouldn't have referred to the navigationController as I had none hooked up.
Hope this helps any of you (amazing what a glass of wine can do! :)