I wonder if anyone knows why an NSPopover does not respond to mouse events (hover, click) when it is opened from a Button inside an NSMenuItem inside an NSMenu.
And if there’s any solution for this problem?
Here’s a little sample view controller that attaches an NSMenu to its view. The menu has a button that opens the popover on click, and some dummy items that are just there to show that they still receive the mouse events even though the popover is above them:
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMenu *menu = [NSMenu new];
/// Some Dummy Items to show that the popover doesn’t accept the mouse.
NSArray *dummyItems = #[#"item1", #"item2", #"item3"];
for (NSString *itemName in dummyItems) {
NSMenuItem *item = [NSMenuItem new];
item.title = itemName;
item.target = self;
item.action = #selector(doNothing:);
[menu addItem:item];
}
/// The button which presents the popover on click
NSMenuItem *item = [NSMenuItem new];
NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 30)];
button.target = self;
button.action = #selector(showPopover:);
button.title = #"Show Popover";
item.view = button;
[menu addItem:item];
[self.view setMenu:menu];
}
- (void)showPopover:(NSButton *)sender {
/// This popover does not receive the mouse events for some reason.
NSPopover *popover = [NSPopover new];
NSViewController *popoverVC = [NSViewController new];
NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 140, 25)];
button.title = #"I’m not clickable";
button.target = self;
button.action = #selector(showPopover:);
popoverVC.view = button;
[popover setContentViewController:popoverVC];
[popover showRelativeToRect:sender.frame ofView:sender.superview preferredEdge:NSMaxYEdge];
[button.window makeKeyWindow]; /// Does not help
[button.window becomeKeyWindow]; /// Does not help
}
- (void)doNothing:(id)sender {}
#end
There is a solution, but it may not be useful depending on what you are trying to do. I was unable to run the code that you posted, but can see that you added a button to both a menu and the popover. The problem appears to be the rectangle that is used to display the popover. For reasons unknown to me, if we try to display the popover based on the frame of the menu item button it is not possible to click on the popover button. However, if popover display is based on the frame of a window contentView based control such as NSPopUpButton or the rectangle of an NSView positioned near the menu the popover button becomes clickable. Menus dropped from the menubar present more of a challenge, due to lack of a nearby rectangle. This demo may be run in an Xcode objc project by deleting the pre-existing main.m code and copy/pasting the following into it. It is also necessary to delete the pre-existing AppDelegate files to avoid duplicate symbols. The demo illustrates the use of both an NSView rectangle and NSPopUpButton frame to display the popover, either of which will allow a clickable popover button. Code that fails is REMmed out.
#import <Cocoa/Cocoa.h>
#interface PopoverController: NSViewController
#end
#implementation PopoverController
#end
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
NSPopover *popover;
NSView *popoverView;
NSMenuItem *menuItem;
NSMenu *menu;
PopoverController *popoverController;
NSPopUpButton *pullDwn;
NSView *menuView;
}
- (void) showPopoverAction:(id)sender;
- (void) menuAction:(id)sender;
- (void) myBtnAction:(id)sender;
- (void) buildMenu;
- (void) buildWnd;
#end
#implementation AppDelegate
- (void) showPopoverAction:(id)sender {
// **** This works **** //
//[popover showRelativeToRect:pullDwn.bounds ofView:pullDwn preferredEdge:NSMaxXEdge];
[popover showRelativeToRect:menuView.bounds ofView:menuView preferredEdge:NSMaxXEdge];
// **** This doesn't **** //
//[popover showRelativeToRect:[menuItem.view bounds] ofView:menuItem.view preferredEdge:NSMaxXEdge];
}
- (void) myBtnAction: (id)sender {
NSBeep();
NSLog(#"Popover button hit.");
NSLog(#"===================");
}
- (void) menuAction: (id)sender {
[popover showRelativeToRect:pullDwn.bounds ofView:pullDwn preferredEdge:NSMaxXEdge];
}
- (void) buildMenu {
NSMenu *menubar = [NSMenu new];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
[NSApp setMainMenu:menubar];
NSMenu *appMenu = [NSMenu new];
NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:#"Quit" action:#selector(terminate:) keyEquivalent:#"q"];
[appMenu addItem:quitMenuItem];
[menuBarItem setSubmenu:appMenu];
}
- (void) buildWnd {
#define _wndW 500
#define _wndH 350
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH )
styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable
backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"NSPopUpButton Menu"];
[window makeKeyAndOrderFront: nil];
// ******* Popover controller ********* //
popoverController = [[PopoverController alloc] init];
// **** Menu View **** //
menuView = [[NSView alloc]initWithFrame:NSMakeRect( 100, _wndH - 242, 130, 30 ) ];
[[window contentView] addSubview:menuView];
// ****** Pull-Down NSPopUpButton ******* //
// First array element (0) becomes the title
NSArray *menuItems = [NSArray arrayWithObjects: #"Pull-Down", #"Item 1", #"Item 2",#"Item 3",nil];
pullDwn = [[NSPopUpButton alloc] initWithFrame:NSMakeRect( 100, _wndH - 142, 130, 30 )];
menu = pullDwn.menu;
menuItem = [NSMenuItem new];
NSButton *myBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 30, 30, 95, 30 )];
[myBtn setBezelStyle:NSBezelStyleRounded ];
[myBtn setTitle: #"Popover"];
[myBtn setAction: #selector (showPopoverAction:)];
menuItem.view = myBtn;
[pullDwn setPullsDown:YES];
[pullDwn addItemsWithTitles:menuItems];
[pullDwn setTarget:self];
[pullDwn setAction:#selector(menuAction:)];
[menu addItem:menuItem];
[[window contentView]addSubview:pullDwn];
// **** NSPopover **** //
popover = [[NSPopover alloc] init];
[popover setContentViewController:popoverController];
[popover setContentSize:NSMakeSize(150, 100)];
//[popover setBehavior:NSPopoverBehaviorTransient];
[popover setBehavior:NSPopoverBehaviorSemitransient];
//[popover setBehavior:NSPopoverBehaviorApplicationDefined];
[popover setAnimates: YES];
[popover setDelegate: self];
// ****** Popover view ****** //
popoverView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 150, 100)];
//******* Popover text field ******* //
NSTextField *ef = [[NSTextField alloc] initWithFrame:NSMakeRect( 20, 20, 110, 24 )];
[ef setStringValue:#"EditFld"];
[ef setAlignment:NSTextAlignmentCenter];
[ef setDrawsBackground:NO];
[popoverView addSubview:ef];
// **** Popover Button **** //
NSButton *popoverBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 20, 50, 95, 24 )];
[popoverBtn setBezelStyle:NSBezelStyleRounded ];
[popoverBtn setTitle: #"Push me"];
popoverBtn.target = self;
popoverBtn.action = #selector(myBtnAction:);
[popoverView addSubview: popoverBtn];
[popoverController setView:popoverView];
// ***** Quit btn ***** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 5, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAutoresizingMask: NSViewMinXMargin];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWnd];
}
- (void) applicationDidFinishLaunching: (NSNotification *)notification {
}
#end
int main () {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
return 0;
}
Related
I'm writing a library to launch operating system dialogs such as file open/save, message dialogs, and prompts from C applications. I can't figure out how to launch a color picker on Mac, for selecting a RGB(A) color value.
This is as far as I've gotten in the Mac implementation of my library in Objective C. I assume I need to launch a NSColorPanel, but I can't find an example of how to do this online.
int launchColorPicker(float* r, float* g, float* b, float* a) {
#autoreleasepool {
NSColorPanel* panel = [NSColorPanel sharedColorPanel];
// ???
*r = ...;
*g = ...;
*b = ...;
*a = ...;
return succeeded;
} // #autoreleasepool
}
The following is an example of a programmatic NSColorPanel in objc. It may be run in Xcode by replacing main.m with the following code and deleting the pre-existing AppDelegate to avoid duplicate symbols.
#import <Cocoa/Cocoa.h>
#interface CustomView : NSView {
NSTextField *redFld;
NSTextField *greenFld;
NSTextField *blueFld;
NSColor *myColor;
}
#property (strong) NSTextField *redFld;
#property (strong) NSTextField *greenFld;
#property (strong) NSTextField *blueFld;
#end
#implementation CustomView
#synthesize redFld;
#synthesize greenFld;
#synthesize blueFld;
- (id)initWithFrame:(NSRect)frameRect {
if ((self = [super initWithFrame:frameRect]) != nil) {
myColor = [NSColor whiteColor];
}
return self;
}
- (void)changeColor:(id)sender {
myColor = [sender color];
[redFld setStringValue:[NSString stringWithFormat:#"r = %f",[[sender color] redComponent]]];
[greenFld setStringValue:[NSString stringWithFormat:#"g = %f",[[sender color] greenComponent]]];
[blueFld setStringValue:[NSString stringWithFormat:#"b = %f",[[sender color] blueComponent]]];
[self setNeedsDisplay:YES];
}
-(void)drawRect:(NSRect)rect {
// **** Background **** //
[myColor set];
[NSBezierPath fillRect:rect];
}
// ----- Use this if you want 0,0 (origin) to be top, left ---- //
// ----- Otherwise origin will be at bottom, left (Unflipped) ----- //
-(BOOL)isFlipped {
return YES;
}
#end
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
NSColorPanel *colorPanel;
NSTextField *redFld;
NSTextField *greenFld;
NSTextField *blueFld;
CustomView *customView;
}
#end
#implementation AppDelegate
-(void) buildMenu{
NSMenu *menubar = [NSMenu new];
[NSApp setMainMenu:menubar];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
NSMenu *appMenu = [NSMenu new];
[menuBarItem setSubmenu:appMenu];
[appMenu addItemWithTitle:#"Quit" action:#selector(terminate:) keyEquivalent:#"q"];
}
-(void) buildWindow {
#define _wndW 500
#define _wndH 550
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH ) styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"Test window"];
[window makeKeyAndOrderFront: nil];
// **** Create an instance of CustomView **** //
customView = [[CustomView alloc]initWithFrame:NSMakeRect( 20, 60, _wndW - 40, _wndH - 120 )];
[[window contentView] addSubview:customView];
// **** NSColorPanel **** //
colorPanel = [NSColorPanel sharedColorPanel];
[NSColorPanel setPickerMode:NSColorPanelModeWheel];
[NSApp orderFrontColorPanel:colorPanel];
[colorPanel setTarget:customView];
[colorPanel setAction:#selector(changeColor:)];
// **** Display Fields **** //
redFld = [[NSTextField alloc] initWithFrame:NSMakeRect( 20, _wndH - 40, 110, 22 )];
[[window contentView] addSubview:redFld];
[redFld setStringValue:#"red"];
[customView setRedFld:redFld];
greenFld = [[NSTextField alloc] initWithFrame:NSMakeRect( 140, _wndH - 40, 110, 22 )];
[[window contentView] addSubview:greenFld];
[greenFld setStringValue:#"green"];
[customView setGreenFld:greenFld];
blueFld = [[NSTextField alloc] initWithFrame:NSMakeRect( 260, _wndH - 40, 110, 22 )];
[[window contentView] addSubview:blueFld];
[blueFld setStringValue:#"blue"];
[customView setBlueFld:blueFld];
// **** Quit btn **** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
-(void) applicationWillFinishLaunching: (NSNotification *)notification
{
[self buildMenu];
[self buildWindow];
}
#end
int main() {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
return 0;
}
Just for your information there are several types of color panels, shown at the touch of a button in the following demo. You'll still have to figure out how to get the values from the user's selection.
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
}
#end
#implementation AppDelegate
-(void) myBtnAction:(id)sender {
[NSColorPanel setPickerMode:[sender tag]];
[NSApp orderFrontColorPanel:nil];
}
-(void) buildMenu {
NSMenu *menubar = [NSMenu new];
[NSApp setMainMenu:menubar];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
NSMenu *appMenu = [NSMenu new];
[menuBarItem setSubmenu:appMenu];
[appMenu addItemWithTitle:#"Quit" action:#selector(terminate:) keyEquivalent:#"q"];
}
-(void) buildWindow {
#define _wndW 380
#define _wndH 420
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH )styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"NSColorPanel Types"];
[window makeKeyAndOrderFront: nil];
// ***** NSBox ***** //
#define _boxW 265
#define _boxH 340
NSBox *boxBtn = [[NSBox alloc]initWithFrame:NSMakeRect( 60, 60, _boxW, _boxH )];
[boxBtn setTitle:#""];
[boxBtn setBorderType:NSBezelBorder];
[[window contentView] addSubview: boxBtn];
// **** Radio Group **** //
NSArray *colorPanelTypes = [NSArray arrayWithObjects:
#"NSNoModeColorPanel",
#"NSGrayModeColorPanel",
#"NSRGBModeColorPanel",
#"NSCMYKModeColorPanel",
#"NSHSBModeColorPanel",
#"NSCustomPaletteModeColorPanel",
#"NSColorListModeColorPanel",
#"NSWheelModeColorPanel",
#"NSCrayonModeColorPanel",
nil];
NSButton *btn[[colorPanelTypes count]];
int count;
count = 0;
#define _btnW 260
#define _btnH 24
#define _spacing 10
#define _left 10
#define _top _boxH - 55
for (unsigned int row = 0; row < [colorPanelTypes count]; row++ ) {
btn[count] = [[NSButton alloc]initWithFrame:NSMakeRect( _left, _top - row*(_btnH + _spacing), _btnW, _btnH )];
[btn[count] setButtonType:NSButtonTypeRadio];
// **** start at minus one **** //
[btn[count] setTag: count-1];
[btn[count] setTitle: [colorPanelTypes objectAtIndex:count]];
[btn[count] setTarget:self];
[btn[count] setAction:#selector(myBtnAction:)];
if (count == 0) {[btn[count] setState:NSControlStateValueOn];}
[boxBtn addSubview: btn[count]];
count++;
}
// **** Quit btn **** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
-(void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWindow];
}
#end
int main() {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
return 0;
}
i am on macOS, objective-c, not iOS. XCode 12.
In a lot of views i set colors like this:
self.menuIconBar.wantsLayer = YES;
self.menuIconBar.layer.backgroundColor = [NSColor colorNamed:#"color_gradient_right"].CGColor;
Whenever the user changes the Appeareance, e.g. to Dark mode, i expect my colors to change according to the Asset setup:
Unfortunately, nothing happens. BUT: The same color applied in IB directly changes as expected. Still i'd need them to change programmatically too.
Then i tried to hook on notifications:
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:#selector(appleInterfaceThemeChangedNotification:) name:#"AppleInterfaceThemeChangedNotification" object:nil];
I receive the notifications, but when i then call the same code like above again, still the wrong color is loaded.
self.menuIconBar.layer.backgroundColor = [NSColor colorNamed:#"color_gradient_right"].CGColor;
Any help appreciated
The following example will change the background color of a custom view depending on the Appearance setting in System Preferences. It may be run in Xcode by creating an objc project, deleting the pre-existing App Delegate, and replacing the code in 'main.m' with the code below:
#import <Cocoa/Cocoa.h>
#interface CustomView : NSView
#end
#implementation CustomView
- (id)initWithFrame:(NSRect)frameRect {
if ((self = [super initWithFrame:frameRect]) != nil) {
// Add initialization code here
}
return self;
}
- (void)drawRect:(NSRect)rect {
}
- (void)viewDidChangeEffectiveAppearance {
NSLog (#"appearance did change.");
NSAppearance *changedAppearance = NSApp.effectiveAppearance;
NSAppearanceName newAppearance = [changedAppearance bestMatchFromAppearancesWithNames:#[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
NSLog (#"new appearance name = %#", newAppearance);
if([newAppearance isEqualToString:NSAppearanceNameDarkAqua]){
[[self layer] setBackgroundColor:CGColorCreateGenericRGB( 1.0, 0.0, 0.0, 1.0 )];
} else {
[[self layer] setBackgroundColor:CGColorCreateGenericRGB( 0.0, 0.0, 1.0, 1.0 )];
}
}
// Use this if you want 0,0 (origin) to be top, left
// Otherwise origin will be at bottom, left (Unflipped)
-(BOOL)isFlipped {
return YES;
}
#end
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
}
- (void) buildMenu;
- (void) buildWindow;
#end
#implementation AppDelegate
- (void) buildMenu {
NSMenu *menubar = [NSMenu new];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
[NSApp setMainMenu:menubar];
NSMenu *appMenu = [NSMenu new];
NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:#"Quit"
action:#selector(terminate:) keyEquivalent:#"q"];
[appMenu addItem:quitMenuItem];
[menuBarItem setSubmenu:appMenu];
}
- (void) buildWindow {
#define _wndW 600
#define _wndH 550
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH )
styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable
backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"Test window"];
[window makeKeyAndOrderFront: nil];
// **** CustomView **** //
CustomView *view = [[CustomView alloc]initWithFrame:NSMakeRect( 20, 60, _wndW - 40, _wndH - 80 )];
[view setWantsLayer:YES];
[[view layer] setBackgroundColor:CGColorCreateGenericRGB( 0.0, 0.0, 1.0, 1.0 )];
[[window contentView] addSubview:view];
// **** Quit btn **** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWindow];
}
- (void) applicationDidFinishLaunching: (NSNotification *)notification {
}
#end
int main() {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
return 0;
}
This worked for me in my NSView subclass:
- (void)awakeFromNib {
self.wantsLayer = YES;// might be unnecessary
}
- (void)viewDidChangeEffectiveAppearance {
self.needsDisplay = YES;
}
- (void)updateLayer {
self.layer.backgroundColor = NSColor.unemphasizedSelectedContentBackgroundColor.CGColor;
}
As of macOS 11 one should use the performAsCurrentDrawingAppearance: instance method and add anything to apply after an appearance change into the given block.
ALL,
I have NSOutlineView with 2 columns. This view is sized so that it is wide enough to cover the whole monitor.
The view contains 2 columns. The second column has a fixed width.
Lets say the width of the screen is 1920 and the width of the second column is 100. Then I want the width of the first column to be 1820.
I.e., column_width1 + column_width2 = screen_width and therefore no scrollbar is visible.
What is the easiest way to do it from the code?
(I'm using Objective-C Cocoa API).
TIA!
EDIT:
After trying the suggestions the only thing I could come up with was the screenshot below.
#define _scrnW [[NSScreen mainScreen] frame].size.width
#define _col1W _scrnW - 100
#define _col2W 100
The following code programmatically creates an NSOutlineView using manual reference counting:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
}
- (void) buildMenu;
- (void) buildWindow;
#end
#implementation AppDelegate
- (void) dealloc {
[window release];
[super dealloc];
}
- (void) buildMenu {
NSMenu *menubar = [[NSMenu new] autorelease];
NSMenuItem *menuBarItem = [[NSMenuItem new] autorelease];
[menubar addItem:menuBarItem];
[NSApp setMainMenu:menubar];
NSMenu *appMenu = [[NSMenu new] autorelease];
NSMenuItem *quitMenuItem = [[[NSMenuItem alloc] initWithTitle:#"Quit"
action:#selector(terminate:) keyEquivalent:#"q"] autorelease];
[appMenu addItem:quitMenuItem];
[menuBarItem setSubmenu:appMenu];
}
- (void) buildWindow {
#define _scrnW [[NSScreen mainScreen] frame].size.width
#define _col1W _scrnW - 100
#define _col2W 100
#define _wndW _scrnW
#define _wndH 550
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH )
styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable
backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"Test window"];
[window makeKeyAndOrderFront: nil];
// ***** NSScrollView to hold OutlineView ****** //
NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:NSMakeRect( 1, 60, _wndW, _wndH - 60 )];
[scrollView setBorderType:NSBezelBorder];
[scrollView setHasVerticalScroller:YES];
[scrollView setHasHorizontalScroller:YES];
[scrollView setAutohidesScrollers:YES];
[scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
// ******* NSOutlineView ******* //
NSOutlineView *outlineView = [[NSOutlineView alloc] initWithFrame: [scrollView bounds]];
[[window contentView] addSubview: outlineView];
[outlineView release];
// ***** OutlineView Controller ****** //
// OutlineViewController *myController = [[OutlineViewController alloc] init];
// [outlineView setDataSource: myController];
// [outlineView setDelegate: myController];
// ***** Column one ***** //
NSTableColumn *colOne = [[NSTableColumn alloc] initWithIdentifier: #"Col1"];
[colOne setEditable: NO];
[colOne setWidth: _col1W];
[colOne setMinWidth: _col1W];
[colOne setMaxWidth: _col1W];
[[colOne headerCell] setStringValue:#"colOne"];
[outlineView addTableColumn: colOne];
// **** Won't see triangles without this ***** //
[outlineView setOutlineTableColumn:colOne];
[colOne release];
// ***** Column two ****** //
NSTableColumn *colTwo = [[NSTableColumn alloc] initWithIdentifier: #"Col2"];
[colTwo setEditable: NO];
[colTwo setWidth: _col2W];
[colTwo setMinWidth: _col2W];
[colTwo setMaxWidth: _col2W];
[[colTwo headerCell] setStringValue:#"colTwo"];
[outlineView addTableColumn: colTwo];
// **** Won't see triangles without this ***** //
[outlineView setOutlineTableColumn:colTwo];
[colTwo release];
// ***** Embed OutlineView into scroll view **** //
[scrollView setDocumentView:outlineView];
[[window contentView] addSubview: scrollView];
[scrollView release];
// ***** Quit btn ***** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 5, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAutoresizingMask: NSViewMinXMargin];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
[quitBtn release];
}
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWindow];
}
- (void) applicationDidFinishLaunching: (NSNotification *)notification {
}
#end
int main () {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[[AppDelegate alloc] init] autorelease];
[application setDelegate:appDelegate];
[application run];
[pool drain];
return 0;
}
I wish to create a window without a nib file which consists entirely of an NSTextView.
It should act as a popup but still be modeless.
So far I have two properties:
#property (strong,nonatomic) NSWindow *consoleWindow;
#property (strong,nonatomic) NSTextView* textView;
Here is what my implementation looks like:
-(void)doubleAction:(NSOutlineView*)sender
{
if(self.currentLogEntry == nil)
{
return;
}
self.consoleWindow = nil;
self.textView = nil;
NSRect windowRect = NSMakeRect(10.0f, 10.0f, 500.0f, 400.0f);
self.consoleWindow = [[NSWindow alloc] initWithContentRect:windowRect
styleMask:( NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask)
backing:NSBackingStoreBuffered defer:NO];
self.textView = [[NSTextView alloc] initWithFrame:[[self.consoleWindow contentView] frame]];
[self.textView setString:self.currentLogEntry.value];
[self.consoleWindow setContentView:self.textView];
[self.consoleWindow makeKeyAndOrderFront:nil];
[self.consoleWindow makeFirstResponder:self.textView];
NSLog(#"Double clicked");
}
Things have been wired up so I have a list of entries, whenever I double click on an entry the selected entry is loaded into self.currentLogEntry and this method is then fired. It works but if I close the window and try to open another entry I get Thread 1:EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
I think it has something to do with the window not being released properly but I have tried a few things like setting the properties to nil whenever the program enters the doubleAction (as you can see in the code) but that doesn't help.
Any help solving this problem is greatly appreciated.
The soulution was to: [self.consoleWindow setReleasedWhenClosed:NO];
Here is how the full code ended out being (only relevant parts):
.h:
#property (strong,nonatomic) NSWindow *consoleWindow;
#property (strong,nonatomic) NSTextView* textView;
#property (strong,nonatomic) NSScrollView* scrollView;
.m:
-(void)doubleAction:(NSOutlineView*)sender
{
if(self.currentLogEntry == nil)
{
return;
}
self.consoleWindow = nil;
self.textView = nil;
self.scrollView = nil;
NSRect windowRect = NSMakeRect(10.0f, 10.0f, 500.0f, 400.0f);
self.consoleWindow = [[NSWindow alloc] initWithContentRect:windowRect
styleMask:( NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask)
backing:NSBackingStoreBuffered defer:NO];
[self.consoleWindow setReleasedWhenClosed:NO];
self.scrollView = [[NSScrollView alloc] initWithFrame:[[self.consoleWindow contentView] frame]];
[self.scrollView setBorderType:NSNoBorder];
[self.scrollView setHasVerticalScroller:YES];
[self.scrollView setHasHorizontalScroller:NO];
[self.scrollView setAutoresizingMask:NSViewWidthSizable |
NSViewHeightSizable];
NSSize contentSize = [self.scrollView contentSize];
self.textView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0,
contentSize.width, contentSize.height)];
[self.textView setMinSize:NSMakeSize(0.0, contentSize.height)];
[self.textView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
[self.textView setVerticallyResizable:YES];
[self.textView setHorizontallyResizable:NO];
[self.textView setAutoresizingMask:NSViewWidthSizable];
[[self.textView textContainer]
setContainerSize:NSMakeSize(contentSize.width, FLT_MAX)];
[[self.textView textContainer] setWidthTracksTextView:YES];
[self.textView setString:self.currentLogEntry.value];
[self.scrollView setDocumentView:self.textView];
[self.consoleWindow setContentView:self.scrollView];
[self.consoleWindow makeKeyAndOrderFront:nil];
[self.consoleWindow makeFirstResponder:self.textView];
NSLog(#"Double clicked");
}
I have several textview in my UIVIew which contained in a scrollview.
First the scollview doesn't not actives when I touched it and want to hide the keyboard after finished the textview editing. So I had build a class name subUIView inherit from scrollview. In that class I overwrite the touch touchbegan events. like the code blow.
But after that the textviewbeingediting function didn't action any more. It's all about touchbegan events calling even when I want to edit the textview.
Could you offer me some advise to solve the problem?Thanks
//
// **subUIView.h**
#import <UIKit/UIKit.h>
#interface subUIView : UIScrollview
{
}
#end
// **subUIView.m**
#import "subUIView.h"
#implementation subUIView
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
self.delegate=self;
//return [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;}
NSInteger a=0;
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event { NSLog(#"touch began ");
// If not dragging, send event to next responder
if (!self.dragging)
{
[self.nextResponder touchesBegan: touches withEvent:event];
}}
in the code blow I alloc scrollview from subUIView and code the textviewbeginediting function.
//
// **ReadExperienceInfoViewController.h**
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#define ExperienceTableName #"pockets"
#class subUIView;
#interface ReadExperienceInfoViewController : UIViewController <UITextFieldDelegate,UITextViewDelegate> {
subUIView *scrollView;
UIView *upView;
UITextField *bookNameTextField;
UITextView *bookExprection;
NSString *getFullDateStr;
BOOL dragging;
}
#property (nonatomic, retain) UIView *upView;
#property (nonatomic, retain) subUIView *scrollView;
//
// **ReadExperienceInfoViewController.m**
//- (void)loadView{
scrollView=[[subUIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
scrollView.contentSize=CGSizeMake(320, 960);
scrollView.backgroundColor=[UIColor colorWithPatternImage :[UIImage imageNamed:#"infomation.png"]];
scrollView.showsVerticalScrollIndicator=YES;
upView=[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
upView=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 960)];
[upView setUserInteractionEnabled:YES];
UILabel *bookName = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 100, 30)];
bookName.backgroundColor = [UIColor clearColor];
bookName.text = #"Title";
bookName.font=[UIFont boldSystemFontOfSize:16];
[upView addSubview:bookName];
[bookName release];
bookNameTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 50, 280, 30)];
bookNameTextField.backgroundColor = [UIColor whiteColor];
bookNameTextField.returnKeyType=UIReturnKeyDone;
bookNameTextField.delegate=self;
[upView addSubview:bookNameTextField];
//lowerView=[[UIView alloc] initWithFrame:CGRectMake(0, 90, 320, 170)];
UILabel *bookExprectionLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 80, 200, 30)];
bookExprectionLabel.text = #"Content";
bookExprectionLabel.font=[UIFont boldSystemFontOfSize:16];
bookExprectionLabel.backgroundColor = [UIColor clearColor];
[upView addSubview:bookExprectionLabel];
[bookExprectionLabel release];
bookExprection = [[UITextView alloc] initWithFrame:CGRectMake(20, 110, 280, 140)];
bookExprection.backgroundColor = [UIColor whiteColor];
bookExprection.font = [UIFont systemFontOfSize:14];
bookExprection.delegate=self;
[upView addSubview:bookExprection];
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Save"
style:UIBarButtonItemStylePlain
target:self action:#selector(updateExperience)]
autorelease];
self.title= bookNameStr;
self.view=scrollView;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
upView.frame=CGRectMake(0, 0, 320, 960);
[UIView commitAnimations];
[bookExprection resignFirstResponder];
[bookNameTextField resignFirstResponder];
}
-(void)textViewDidBeginEditing:(UITextView *)textView{
NSLog(#"texview dig begin editing");
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
if (textView==bookExprection) {
upView.frame=CGRectMake(0, -80, 320, 0);
}
[UIView commitAnimations];
}
Your subUIView.h File must implement the UITextFieldDelegate Protocol, so your viewcontroller responds to the delegate methods for the text field.
Same for the Scroll view, you have to implement the ScrollView
//subUIView.h
#interface subUIView : UIViewController <UIScrollViewDelegate, UITextFieldDelegate>{
//do stuff
}
Then of course you must write scrollView.delegate = self; so that the viewcontroller responds to the delegatemethods for the scroll view.
Hope this helps