I'm trying to create a NSLabel for my osx app however Xcode is not recognizing the type "NSLabel" as valid and is suggesting I try "NSPanel" instead.
In the header file I have the following imports:
#import <Cocoa/Cocoa.h>
#import <AppKit/AppKit.h>
How do I fix this? Is there another file I need to import?
There is no label class (NSLabel) on OS X. You have to use NSTextField instead, remove the bezel and make it non editable:
[textField setBezeled:NO];
[textField setDrawsBackground:NO];
[textField setEditable:NO];
[textField setSelectable:NO];
Swift 4.2 🔸
open class NSLabel: NSTextField {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.isBezeled = false
self.drawsBackground = false
self.isEditable = false
self.isSelectable = false
}
}
I had the same question, following DrummerB advice I created this NSLabel class.
Header
//
// NSLabel.h
//
// Created by Axel Guilmin on 11/5/14.
//
#import <AppKit/AppKit.h>
#interface NSLabel : NSTextField
#property (nonatomic, assign) CGFloat fontSize;
#property (nonatomic, strong) NSString *text;
#end
Implementation
//
// NSLabel.m
//
// Created by Axel Guilmin on 11/5/14.
//
#import "NSLabel.h"
#implementation NSLabel
#pragma mark INIT
- (instancetype)init {
self = [super init];
if (self) {
[self textFieldToLabel];
}
return self;
}
- (instancetype)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (self) {
[self textFieldToLabel];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self textFieldToLabel];
}
return self;
}
#pragma mark SETTER
- (void)setFontSize:(CGFloat)fontSize {
super.font = [NSFont fontWithName:self.font.fontName size:fontSize];
}
- (void)setText:(NSString *)text {
[super setStringValue:text];
}
#pragma mark GETTER
- (CGFloat)fontSize {
return super.font.pointSize;
}
- (NSString*)text {
return [super stringValue];
}
#pragma mark - PRIVATE
- (void)textFieldToLabel {
super.bezeled = NO;
super.drawsBackground = NO;
super.editable = NO;
super.selectable = YES;
}
#end
You'll need to #import "NSLabel.h" to use it, but I think it's more clean.
Related
So I got a Window:
Window.xib
I got a WindowController too:
WindowController.h reads:
#import <Cocoa/Cocoa.h>
#interface MainWindowController : NSWindowController
{
IBOutlet NSView *firstView;
IBOutlet NSView *secondView;
IBOutlet NSView *thirdView;
int currentViewTag;
}
-(IBAction)switchView:(id)sender;
#end
And the WindowController.m reads:
#import "MainWindowController.h"
#interface MainWindowController ()
#end
#implementation MainWindowController
-(id)init
{
self = [super initWithWindowNibName:#"MainWindow"];
if (self){
// Initialization code here
}
return self;
}
//- (void)windowDidLoad {
// [super windowDidLoad];
//
// // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
//}
#pragma mark - Custom view drawing
-(NSRect)newFrameForNewContentView:(NSView *)view
{
NSWindow *window = [self window];
NSRect newFrameRect = [window frameRectForContentRect:[view frame]];
NSRect oldFrameRect = [window frame];
NSSize newSize = newFrameRect.size;
NSSize oldSize = oldFrameRect.size;
NSRect frame = [window frame];
frame.size = newSize;
frame.origin.y -= (newSize.height - oldSize.height);
return frame;
}
-(NSView *)viewForTag:(int)tag{
NSView *view = nil;
if (tag == 0) {
view = firstView;
} else if (tag == 1) {
view = secondView;
} else {
view = thirdView;
}
return view;
}
-(BOOL) validateToolbarItem:(NSToolbarItem *)item
{
if ([item tag] == currentViewTag) return NO;
else return YES;
}
-(void)awakeFromNib
{
[[self window] setContentSize:[firstView frame].size];
[[[self window] contentView]addSubview:firstView];
[[[self window] contentView]setWantsLayer:YES];
}
-(IBAction)switchView:(id)sender
{
int tag = [sender tag];
NSView *view = [self viewForTag:tag];
NSView *previousView = [self viewForTag:currentViewTag];
currentViewTag = tag;
NSRect newFrame = [self newFrameForNewContentView:view];
[NSAnimationContext beginGrouping];
if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask)
[[NSAnimationContext currentContext] setDuration:1.0];
[[[[self window]contentView]animator]replaceSubview:previousView with:view];
[[[self window]animator]setFrame:newFrame display:YES];
[NSAnimationContext endGrouping];
}
#end
The problem I have is that when i switch tabs in my app, the custom view (and there are three of different sizes) draw differently each time. Look at the screenshots, all of the numbers should be centre aligned but they are sometimes and others not. Can anyone see what my error is please?
I will also add that all of the actions have been correctly configured + the code works perfectly if the custom view size is the same all the time.
The view that works
The view that almost works
Again pointing out that in my .xib all of the numbers are aligned to 0x and 0y axis.
Appdelegate.h
#import <Cocoa/Cocoa.h>
#class MainWindowController;
#interface AppDelegate : NSObject <NSApplicationDelegate> {
MainWindowController *mainWindowController;
}
#end
Appdelegate.m
#interface AppDelegate ()
#property (nonatomic) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
-(void)awakeFromNib
{
if(!mainWindowController){
mainWindowController = [[MainWindowController alloc]init];
}
[mainWindowController showWindow:nil];
}
#end
In Interface Builder, make sure to disable the "autoresizes subviews" checkbox of the default view of your window
I cannot find a simple example of how to use an NSTextField to edit it's contents in place.
Exactly like in the Finder - you're able to click, and with a short delay the text field becomes editable.
It seems like it's some combination of the textField, it's cell, and the fieldEditor? Problem is I can't find the most basic example of how to do it.
I've tried subclassing NSTextField with a couple different tests but it hasn't worked:
#import "GWTextField.h"
#implementation GWTextField
- (id) initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
return self;
}
- (void) mouseDown:(NSEvent *)theEvent {
[super mouseDown:theEvent];
[self.cell editWithFrame:self.frame inView:self.superview editor:[self.cell fieldEditorForView:self] delegate:self event:theEvent];
//[self setEditable:TRUE];
//[self setSelectable:TRUE];
//[self selectText:nil];
[NSTimer scheduledTimerWithTimeInterval:.3 target:self selector:#selector(edit:) userInfo:nil repeats:FALSE];
}
- (void) edit:(id) sende {
NSLog(#"edit");
[[NSApplication sharedApplication].mainWindow makeFirstResponder:self];
[self selectText:nil];
}
#end
Any ideas?
Here's another solution with no NSCell - one user pointed out that NSCell is deprecated and will at some point be gone.
#import <Cocoa/Cocoa.h>
#interface EditTextField : NSTextField <NSTextDelegate,NSTextViewDelegate,NSTextFieldDelegate>
#property BOOL isEditing;
#property BOOL commitChangesOnEscapeKey;
#property BOOL editAfterDelay;
#property CGFloat delay;
#end
----
#import "EditTextField.h"
#interface EditTextField ()
#property NSObject <NSTextFieldDelegate,NSTextViewDelegate> * userDelegate;
#property NSString * originalStringValue;
#property NSTimer * editTimer;
#property NSTrackingArea * editTrackingArea;
#end
#implementation EditTextField
- (id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
[self defaultInit];
return self;
}
- (id) initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
[self defaultInit];
return self;
}
- (id) init {
self = [super init];
[self defaultInit];
return self;
}
- (void) defaultInit {
self.delay = .8;
}
- (void) mouseDown:(NSEvent *) theEvent {
if(theEvent.clickCount == 2) {
[self startEditing];
} else {
[super mouseDown:theEvent];
if(self.editAfterDelay) {
[self startTracking];
self.editTimer = [NSTimer scheduledTimerWithTimeInterval:.8 target:self selector:#selector(startEditing) userInfo:nil repeats:FALSE];
}
}
}
- (void) startTracking {
if(!self.editTrackingArea) {
self.editTrackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:NSTrackingMouseEnteredAndExited|NSTrackingMouseMoved|NSTrackingActiveInActiveApp|NSTrackingAssumeInside|NSTrackingInVisibleRect owner:self userInfo:nil];
}
[self addTrackingArea:self.editTrackingArea];
}
- (void) mouseExited:(NSEvent *)theEvent {
[self.editTimer invalidate];
self.editTimer = nil;
}
- (void) mouseMoved:(NSEvent *) theEvent {
[self.editTimer invalidate];
self.editTimer = nil;
}
- (void) startEditing {
id firstResponder = self.window.firstResponder;
if([firstResponder isKindOfClass:[NSTextView class]]) {
NSTextView * tv = (NSTextView *)firstResponder;
if(tv.delegate && [tv.delegate isKindOfClass:[EditTextField class]]) {
EditTextField * fr = (EditTextField *)tv.delegate;
[fr stopEditingCommitChanges:FALSE clearFirstResponder:FALSE];
}
}
if(self.delegate != self) {
self.userDelegate = (NSObject <NSTextFieldDelegate,NSTextViewDelegate> *)self.delegate;
}
self.isEditing = TRUE;
self.delegate = self;
self.editable = TRUE;
self.originalStringValue = self.stringValue;
[self.window makeFirstResponder:self];
}
- (void) stopEditingCommitChanges:(BOOL) commitChanges clearFirstResponder:(BOOL) clearFirstResponder {
self.editable = FALSE;
self.isEditing = FALSE;
self.delegate = nil;
[self removeTrackingArea:self.editTrackingArea];
if(!commitChanges) {
self.stringValue = self.originalStringValue;
}
if(clearFirstResponder) {
[self.window makeFirstResponder:nil];
}
}
- (void) cancelOperation:(id) sender {
if(self.commitChangesOnEscapeKey) {
[self stopEditingCommitChanges:TRUE clearFirstResponder:TRUE];
} else {
[self stopEditingCommitChanges:FALSE clearFirstResponder:TRUE];
}
}
- (BOOL) textView:(NSTextView *) textView doCommandBySelector:(SEL) commandSelector {
BOOL handlesCommand = FALSE;
NSString * selector = NSStringFromSelector(commandSelector);
if(self.userDelegate) {
if([self.userDelegate respondsToSelector:#selector(control:textView:doCommandBySelector:)]) {
handlesCommand = [self.userDelegate control:self textView:textView doCommandBySelector:commandSelector];
} else if([self.userDelegate respondsToSelector:#selector(textView:doCommandBySelector:)]) {
handlesCommand = [self.userDelegate textView:textView doCommandBySelector:commandSelector];
}
if(!handlesCommand) {
if([selector isEqualToString:#"insertNewline:"]) {
[self stopEditingCommitChanges:TRUE clearFirstResponder:TRUE];
handlesCommand = TRUE;
}
if([selector isEqualToString:#"insertTab:"]) {
[self stopEditingCommitChanges:TRUE clearFirstResponder:FALSE];
handlesCommand = FALSE;
}
}
} else {
if([selector isEqualToString:#"insertNewline:"]) {
[self stopEditingCommitChanges:TRUE clearFirstResponder:TRUE];
handlesCommand = TRUE;
}
if([selector isEqualToString:#"insertTab:"]) {
[self stopEditingCommitChanges:TRUE clearFirstResponder:FALSE];
handlesCommand = FALSE;
}
}
return handlesCommand;
}
#end
I built a re-usable NSTextField subclass you can use for edit in place functionality. http://pastebin.com/QymunMYB
I came up with a better solution to the edit in place problem. I believe this is how to properly do edit in place with NSCell. Please show and tell if this is wrong.
#import <Cocoa/Cocoa.h>
#interface EditTextField : NSTextField <NSTextDelegate>
#end
---
#import "EditTextField.h"
#implementation EditTextField
- (void) mouseDown:(NSEvent *)theEvent {
if(theEvent.clickCount == 2) {
self.editable = TRUE;
NSText * fieldEditor = [self.window fieldEditor:TRUE forObject:self];
[self.cell editWithFrame:self.bounds inView:self editor:fieldEditor delegate:self event:theEvent];
} else {
[super mouseDown:theEvent];
}
}
- (void) cancelOperation:(id)sender {
[self.cell endEditing:nil];
self.editable = FALSE;
}
- (BOOL) textView:(NSTextView *) textView doCommandBySelector:(SEL) commandSelector {
NSString * selector = NSStringFromSelector(commandSelector);
if([selector isEqualToString:#"insertNewline:"]) {
NSText * fieldEditor = [self.window fieldEditor:TRUE forObject:self];
[self.cell endEditing:fieldEditor];
self.editable = FALSE;
return TRUE;
}
return FALSE;
}
#end
In my application I have two text fields - one non editable, and second, hidden, editable, and activates title editing by calling:
[self addSubview:windowTitle];
[windowTitleLabel removeFromSuperview];
[self.window makeFirstResponder:windowTitle];
This is called from mouseUp: on view behind the label.
I don't remember why I needed to have two text fields (i didn't know Cocoa good that time), probably it will work even without label swapping.
I'm attempting to create this checkbutton and then call that check button where ever I'd like in my project and have it work by adding an image like it's supposed to but I can't even get it to NSLog...for testing purposes...
Below are the button .h and .m files. and the view that I'm calling the button into and trying to use...
CheckButton.h
#import
#interface CheckButton : UIButton {
BOOL _checked;
}
#property (nonatomic, setter=setChecked:) BOOL checked;
-(void) setChecked:(BOOL)check;
#end
CheckButton.m
#import "CheckButton.h"
#implementation CheckButton
#synthesize checked = _checked;
-(id) init {
if (self=[super init]) {
self.checked = NO;
[self addTarget:self action:#selector(setChecked:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(void) awakeFromNib {
self.checked = NO;
[self addTarget:self action:#selector(OnCheck:) forControlEvents:UIControlEventTouchUpInside];
}
- (void) setChecked:(BOOL)check{
_checked = check;
if(_checked) {
UIImage *img = [UIImage imageNamed:#"check.png"];
[self setImage:img forState:UIControlStateNormal];
NSLog(#"Checked");
} else {
UIImage *img = [UIImage imageNamed:#"uncheck.png"];
[self setImage:img forState:UIControlStateNormal];
NSLog(#"UnChecked");
}
}
-(void) OnCheck:(id) sender {
self.checked = _checked;
}
#end
GoToBedPopUp.h
#import "CheckButton.h"
#interface GoToBedPopup : PopupContainer{
IBOutlet CheckButton *checkboxButton;
}
// Checkboxes - (CheckButton)
#property (nonatomic, strong) CheckButton *checkHR;
#end
GoToBedPopUp.m
#import "GoToBedPopup.h"
#implementation GoToBedPopup
#synthesize checkHR, checkO2, checkMovement, checkNoise, checkSkinTemp;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
// Checkbox Button
checkHR = [[CheckButton alloc] initWithFrame:CGRectMake(17, 31, 23, 23)];
return self;
}
hi i tested your code every thing okay u just change only one thing....
- (void) setChecked:(BOOL)check{
_checked = !check; //** this is the only one change needed
if(_checked) {
UIImage *img = [UIImage imageNamed:#"Check.png"];
[self setImage:img forState:UIControlStateNormal];
NSLog(#"Checked");
} else {
UIImage *img = [UIImage imageNamed:#"Uncheck.png"];
[self setImage:img forState:UIControlStateNormal];
NSLog(#"UnChecked");
}
}
Sample project here
What does this Expected expression error mean on this line of code:
[super setNilValueForKey:key];
Code:
#import "Person.h"
#implementation Person
- (id) init
{
self = [super init];
expectedRaise = 5.0;
personName = #"New Person";
return self;
}
- (void)dealloc
{
[personName release];
[super dealloc];
}
- (void)setNilValueForKey:(NSString *)key
{
if ([key isEqual:#"expectedRaise"])
{
[self setExpectedRaise:0.0];
}
else
{
[super setNilValueForKey:key];
}
}
#synthesize personName;
#synthesize expectedRaise;
#end
.h:
#import <Foundation/Foundation.h>
#interface Person : NSObject
{
NSString *personName;
float expectedRaise;
}
#property (readwrite, copy) NSString *personName;
#property (readwrite) float expectedRaise;
#end
If you really got
[super setNilValueForKey:<#key#>];
in your code, the solution should be as simple as
[super setNilValueForKey:key];
isEqual is the wrong method since I wanted to compare to a string value isEqualToString is correct.
- (void)setNilValueForKey:(NSString *)key
{
if ([key isEqualToString:#"expectedRaise"])
{
[self setExpectedRaise:0.0];
}
else
{
[super setNilValueForKey:key];
}
}
Am getting error ("switchChanged" undeclared) in the implementation file, but can't find the problem. Can you help me?
TIA
ViewController.m
#import "Control_FunViewController.h"
#implementation Control_FunViewController
#synthesize nameField;
#synthesize numberField;
#synthesize sliderLabel;
#synthesize leftSwitch;
#synthesize rightSwitch;
#synthesize doSomethingButton;
-(IBAction)sliderChanged:(id)sender
{
UISlider *slider = (UISlider *)sender;
int progressAsInt = (int)(slider.value + 0.5f);
NSString *newText = [[NSString alloc] initWithFormat:#"%d",progressAsInt];
sliderLabel.text = newText;
[newText release];
}
-(IBAction)textFieldDoneEditing:(id)sender
{
[sender resignFirstResponder];
}
-(IBAction)backgroundTap:(id)sender
{
[nameField resignFirstResponder];
[numberField resignFirstResponder];
}
-(IBAction)toggleControls:(id)sender
{
if ([sender selectedSegmentIndex] == kSwitchesSegmentIndex)
{
leftSwitch.hidden = NO;
rightSwitch.hidden = NO;
doSomethingButton.hidden = YES;
}
else {
leftSwitch.hidden =YES;
rightSwitch.hidden =YES;
doSomethingButton.hidden = NO;
}
-(IBAction)switchChanged:(id)sender
{
UISwitch *whichSwitch = (UISwitch *)sender;
BOOL setting = whichSwitch.isOn;
[leftSwitch setOn:setting animated:YES];
[rightSwitch setOn:setting animated:YES];
}
-(IBAction)buttonPressed
{
//TODO: Implement Action Sheet and Alert
}
}
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[nameField release];
[numberField release];
[sliderLabel release];
[leftSwitch release];
[rightSwitch release];
[doSomethingButton release];
[super dealloc];
}
#end
ViewController.h
#import <UIKit/UIKit.h>
#define kSwitchesSegmentIndex 0
#interface Control_FunViewController : UIViewController {
UITextField *nameField;
UITextField *numberField;
UILabel *sliderLabel;
UISwitch *leftSwitch;
UISwitch *rightSwitch;
UIButton *doSomethingButton;
}
#property(nonatomic,retain)IBOutlet UITextField *nameField;
#property(nonatomic,retain)IBOutlet UITextField *numberField;
#property(nonatomic,retain)IBOutlet UILabel *sliderLabel;
#property(nonatomic,retain)IBOutlet UISwitch *leftSwitch;
#property(nonatomic,retain)IBOutlet UISwitch *rightSwitch;
#property(nonatomic,retain)IBOutlet UIButton *doSomethingButton;
-(IBAction)textFieldDoneEditing:(id)sender;
-(IBAction)backgroundTap:(id)sender;
-(IBAction)sliderChanged:(id)sender;
-(IBAction)toggleControls:(id)sender;
-(IBAction)switchChanged:(id)sender;
-(IBAction)buttonPressed;
#end
The colon (:) is part of the name of the method, but you haven't included it in the error message. It may be that you just forgot, but if you're calling -switchChanged (no colon) from somewhere, or if you connected a control using the action -switchChanged (no colon), that's the problem. Perhaps you added the colon and sender parameter later on?