I am using a Toggle button in XIB,
in AwakeFromNib :I set title and BG image of the button.
Code works very well till OSX 10.10.5 and below but on higher versions when I click on button the text gets removed.
Set attributed Title
[self.deleteButton setWantsLayer:YES];
self.deleteButton.layer.backgroundColor = [self setButtonBGColor];
[self setButtonTitleFor:self.deleteButton
toString:#"Delete"];
[self.deleteButton setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay];
- (void)setButtonTitleFor:(NSButton*)button toString:(NSString*)title
{
NSAttributedString *attrString = [self getAttributedStringForString:title];
[button setAttributedTitle:attrString];
}
Any idea hat should be done.
So finally got it right
Subclassing NSButtonCell helped me out
RPButtonTextTopCell.h
#interface RPButtonTextTopCell : NSButtonCell
#end
RPButtonTextTopCell.m
#implementation RPButtonTextTopCell
-(id) init
{
self = [super init];
if (self) {
}
return self;
}
-(id) initWithCoder:(NSCoder *)decoder
{
return [super initWithCoder:decoder];
}
-(id) initTextCell:(NSString *)string
{
return [super initTextCell:string];
}
-(NSRect)titleRectForBounds:(NSRect)theRect
{
NSRect titleFrame = [super titleRectForBounds:theRect];
NSSize titleSize = [[self attributedStringValue] size];
titleFrame.origin.y = (theRect.origin.y - (theRect.size.height-titleSize.height)*0.5) + 5;
return titleFrame;
}
#end
Utilizing This Custom Class
RPButtonTextTopCell *deleteCell = [[RPButtonTextTopCell alloc] init];
deleteCell.backgroundColor = [self setNSButtonBGColor];
[self.deleteButton setCell:deleteCell];
[self setButtonTitleFor:self.deleteButton
toString:#"Delete"];
[self.deleteButton setAction:#selector(deleteButtonClicked:)];
[self.deleteButton setTarget:self];
and its solved....
Related
In building a custom NSButton, I've run into a problem handling highlight behavior. After clicking down on the button, holding, and dragging the cursor outside the button's bounds, mouseExited: and mouseEntered: events are not delivered. I understand the reason why, because in mouseDown: calling [super mouseDown:event]; will block until the click is released.
In researching this I came across this Stack Overflow post which describes the same problem. The solution noted is to add NSTrackingEnabledDuringMouseDrag to the NSTrackingArea options, which I have done, yet I continue to see this problem. I tried the other proposed solution with handling the next events in a loop, but this resulted in odd behavior. The button text color turns black on mouse down instead of highlighting the dimmed color, and it doesn't unhighlight upon releasing the mouse, it remains black.
I am using Xcode 9.3, running on macOS 10.13.4.
Here is my NSButton subclass:
#interface BorderlessButton : NSButton {
NSColor *_tempColor;
}
#property (strong, nonatomic) NSColor *color;
#end
#interface BorderlessButton ()
#property (nonatomic) BOOL pressed;
#end
#implementation BorderlessButton
- (id)init {
if (self = [super init]) {
[self setUp];
}
return self;
}
- (id)initWithFrame:(NSRect)frameRect {
if (self = [super initWithFrame:frameRect]) {
[self setUp];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self setUp];
}
return self;
}
- (void)setUp {
_color = [NSColor redColor];
[self setTitle:self.title];
[self setButtonType:NSButtonTypeMomentaryChange];
[self setBordered:NO];
NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:self.bounds
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingEnabledDuringMouseDrag
owner:self
userInfo:nil];
[self addTrackingArea:area];
}
- (void)setTitle:(NSString *)title {
[super setTitle:title];
NSMutableAttributedString *colorTitle = [[NSMutableAttributedString alloc] initWithAttributedString:[self attributedTitle]];
[colorTitle addAttributes:#{NSFontAttributeName: self.font, NSForegroundColorAttributeName: self.color} range:NSMakeRange(0, [colorTitle length])];
[self setAttributedTitle:colorTitle];
}
- (void)setColor:(NSColor *)color {
_color = color;
[self setTitle:self.title];
}
- (void)mouseDown:(NSEvent *)event {
self.pressed = YES;
[self highlight:YES];
[super mouseDown:event]; // this blocks until released
[self mouseUp:event];
}
- (void)mouseUp:(NSEvent *)event {
self.pressed = NO;
[self highlight:NO];
[super mouseUp:event];
}
//FIXME: Not called after mouse press down and hold then exit
- (void)mouseExited:(NSEvent *)event {
if (self.pressed) {
[self highlight:NO];
}
[super mouseExited:event];
}
- (void)mouseEntered:(NSEvent *)event {
if (self.pressed) {
[self highlight:YES];
}
[super mouseEntered:event];
}
- (void)highlight:(BOOL)flag {
if (flag) {
if (self.isEnabled) {
NSColor *dimmedColor = [self dimmedColor];
_tempColor = _color;
self.color = dimmedColor;
[self setTitle:self.title];
}
} else {
if (self.isEnabled) {
self.color = _tempColor;
[self setTitle:self.title];
}
}
}
- (NSColor *)dimmedColor {
return [self.color colorWithAlphaComponent:0.5];
}
#end
My problem is that the subviews of my scrollView respond to mouse event even if they are not visible (out of the bounds of the visible part of the scrollView).
I've got this architecture :
CustomView *view01 = [[CustomView alloc] init];
CustomView *view02 = [[CustomView alloc] init];
NSView *contentView = [[NSView alloc] init];
[contentView addSubview:view01];
[contentView addSubview:view02];
NSSCrollView *scrollView = [[NSScrollView alloc] init];
scrollView setDocumentView:contentView];
With the CustomView implementation :
#import "CustomView.h"
#interface CustomView ()
{
NSTrackingArea *trackingArea;
}
#end
#implementation CustomView
-(id)initWithFrame:(NSRect)contentRect
{
if(self = [super initWithFrame:(NSRect)contentRect])
{
[self setFrame:contentRect];
}
return self;
}
- (void)mouseDown: (NSEvent*)event
{
NSLog(#"mouseDown:");
[NSApp preventWindowOrdering];
}
- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)evt
{
return YES;
}
-(BOOL)acceptsFirstResponder
{
return YES;
}
-(void)updateTrackingAreas
{
if(trackingArea != nil)
{
[self removeTrackingArea:trackingArea];
}
int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways);
trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
options:opts
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
}
- (void)mouseEntered:(NSEvent *)theEvent
{
NSLog(#"mouseEntered:");
}
- (void)mouseExited:(NSEvent *)theEvent
{
NSLog(#"mouseExited:");
}
- (NSView *)hitTest:(NSPoint)aPoint
{
return NSPointInRect(aPoint, self.frame) ? self : nil;
}
#end
I finally found the problem.
I needed to add the option NSTrackingInVisibleRect in the method :
-(void)updateTrackingAreas
{
if(trackingArea != nil)
{
[self removeTrackingArea:trackingArea];
}
int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways| NSTrackingInVisibleRect);
trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
options:opts
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
}
Then, when a part of the CustomView are outside the scrollView, it doesn't respond to mouseEntered and mouseExited.
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
Im having this problem for weeks already. because my app needs a function that will create a UITextfield, undo, and delete the UITexfield. my code create textfield anywhere on the view when you tap it. and it also can undo the last textfield that been created when pressed button undo, it can be move, scale, rotate also, but after i created another new textfield, the old one was been attached to the view. that is why when i long pressed the old textfield, it can be deleted only the new one that was been created can be delete. what will i do how to make that old textfield deleted?here my code.
ViewController.h
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface ViewController : UIViewController<UITextFieldDelegate, UIGestureRecognizerDelegate>
{
NSMutableArray *textfieldform;//array for textfield
UITextField *textField1;//a textfield
CGPoint prevPanPoint;// float for moving the textfield anywhere
float prevPinchScale;// float for pinching the textfield
float prevRotation;// float for rotating the textfield
}
#property (nonatomic, retain) NSMutableArray *textfieldform;//array for creating multiple textfield
-(IBAction) undo;
- (IBAction)handleTap2:(UITapGestureRecognizer *)recognizer;
-(IBAction)panGestureAction:(UIPanGestureRecognizer *)pan;
- (IBAction)scaleImage:(UIPinchGestureRecognizer *)recognizer;
- (IBAction)rotateImage:(UIRotationGestureRecognizer *)recognizer;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize textfieldform;
- (void)viewDidLoad{
[super viewDidLoad];
//textfieldform = [[NSMutableArray alloc] init];
// Do any additional setup after loading the view, typically from a nib.
textfieldform = [NSMutableArray arrayWithCapacity:0];//array of the textfield
}
//connected to the self.view
-(IBAction) longPressGesture:(UIGestureRecognizer*)gesture{
[textfieldform removeObject:textField1];
[textField1 removeFromSuperview];
NSLog(#"baaaaaaam!");
}
//make the textfield move to any direction in the self.view
-(IBAction)panGestureAction:(UIPanGestureRecognizer *)pan {
CGPoint translation = [pan translationInView:self.view];
textField1.transform = CGAffineTransformTranslate(textField1.transform, translation.x, translation.y);
[pan setTranslation:CGPointZero inView:self.view];
}
//to make the use of gesture simultaneously within the view
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
//to make the use of gesture simultaneously
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if([touch.view isKindOfClass:[UIControl class]]) {
return YES;
}
return NO;
}
//to pinch the textfield using 2 fingers.
- (IBAction)scaleImage:(UIPinchGestureRecognizer *)recognizer{
textField1.transform = CGAffineTransformScale(textField1.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1;
}
//to rotate the textfield using 2 fingers
- (IBAction)rotateImage:(UIRotationGestureRecognizer *)recognizer{
textField1.transform = CGAffineTransformRotate(textField1.transform, recognizer.rotation);
recognizer.rotation = 0;
}
//to remove the last textfield that was been created
-(IBAction)undo{
UITextField *textFieldToRemove = [textfieldform lastObject];
if (textFieldToRemove) {
[textfieldform removeObject:textFieldToRemove];
[textFieldToRemove removeFromSuperview];
}
}
// for the editing of the textfield
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
NSLog(#"textFieldShouldBeginEditing");
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
NSLog(#"textFieldDidBeginEditing");
[textField1 setBackgroundColor:[UIColor colorWithRed:(248/255.0) green:(248/255.0) blue:(255/255.0) alpha:1.0]];
textField1.borderStyle = UITextBorderStyleRoundedRect;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
NSLog(#"textFieldShouldEndEditing");
textField.backgroundColor = [UIColor clearColor];
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField{
NSLog(#"textFieldDidEndEditing");
[textField1 setBackgroundColor:[UIColor clearColor]];
textField1.borderStyle = UITextBorderStyleNone;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSLog(#"textField:shouldChangeCharactersInRange:replacementString:");
if ([string isEqualToString:#"#"]) {
return NO;
}
else {
return YES;
}
}
- (BOOL)textFieldShouldClear:(UITextField *)textField{
NSLog(#"textFieldShouldClear:");
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
NSLog(#"textFieldShouldReturn:");
if (textField.tag == textfieldform.count) {
textField1 = (UITextField *)[self.view viewWithTag:textfieldform.count];
[textField1 becomeFirstResponder];
}
else {
[textField resignFirstResponder];
}
return YES;
}
//when tap the user can create a textfield on any direction, it create many different textfield. according to how many tap you do on the view
- (IBAction)handleTap2:(UITapGestureRecognizer *)recognizer{
if (recognizer.state == UIGestureRecognizerStateEnded){
CGPoint point = [recognizer locationInView:[self view]];
textField1 = [[UITextField alloc] init];
textField1.borderStyle = UITextBorderStyleLine;
[textField1 setAdjustsFontSizeToFitWidth:YES];
[textField1 setText:#"TextField"];
CGRect frame ;
frame.origin.x = point.x;
frame.origin.y = point.y;
frame.size.width=200;
frame.size.height=40;
textField1.frame=frame;
textField1.autocorrectionType = UITextAutocorrectionTypeNo;
textField1.keyboardType = UIKeyboardTypeDefault;
textField1.returnKeyType = UIReturnKeyDefault;
textField1.clearButtonMode = UITextFieldViewModeWhileEditing;
textField1.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField1.delegate = self;
textField1.tag = textfieldform.count;
[textfieldform addObject:textField1];
[self.view addSubview:textField1];
[textField1 setAdjustsFontSizeToFitWidth:YES];
}
}
- (void)viewDidUnload{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
You are using wrong method DoAction as selector, instead use LongPressgesture method
Update your code like following:
- (void)viewDidLoad{
[super viewDidLoad];
textfieldform = [[NSMutableArray alloc] init];
textField1 = ... // Initialize textfield
UILongPressGestureRecognizer *holdRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(LongPressgesture:)];
[holdRecognizer setMinimumPressDuration:2];
[holdRecognizer setDelegate:self];
[textField1 addGestureRecognizer:holdRecognizer];
}
- (void)LongPressgesture:(UILongPressGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateEnded) {
//[textfieldform removeObject: recognizer.view];
[recognizer.view removeFromSuperview];
NSLog(#"Long press Ended .................");
}
else {
NSLog(#"Long press detected .....................");
}
}
I am using an NSToolbar and NSWindowController to change the views of an NSWindow. When the toolbar items are selected, the view is successfully changed for the window and the window changes it's size according to the view size. Upon the initial loading of the window, the contents of the view appear as expected. However, once a toolbar item is selected the contents of the new view are not visible and when the view is switched back to the original view, it's contents are no longer visible either. Not sure what is causing this so any help would be appreciated.
#import <Cocoa/Cocoa.h>
#interface WindowController : NSWindowController {
IBOutlet NSView *firstView;
IBOutlet NSView *secondView;
int currentViewTag;
}
- (IBAction)switchView:(id)sender;
#end
and
#import "WindowController.h"
#interface WindowController ()
#end
#implementation WindowController
- (id)init {
self = [super initWithWindowNibName:#"WindowController"];
if (self) {
}
return self;
}
- (void)windowDidLoad {
[super windowDidLoad];
}
- (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;
switch (tag) {
case 0:
view = firstView;
break;
case 1:
view = secondView;
break;
}
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 {
double 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
I tried out your code, and what I saw was not that the views disappeared, but that they were positioned wrongly and moved out of view. I fixed this by turning off auto layout in IB, and deselecting all the struts and springs in the size inspector. I also deselected the window's "restorable" property so that if you closed the program with view 2 visible, and reopened, the window (with view 1 inside) would be the correct size for view 1.