How to make NSTextField use custom subclass of NSTextFieldCell? - objective-c

I've been looking for a solution to make my NSTextField bottom-aligned and I've found this and adjusted it for my needs. So now I have this custom NSTextFieldCell but how do I tell my NSTextFields to use this class (programmatically)?

Since you ask how to do it programmatically, you can also use the setCellClass: method on your NSTextField subclass. Call it in the load or initialize class methods:
+(void)load
{
[self setCellClass:[MyTextFieldCell class]];
}
It will not have any bearing on your text fields defined in Interface Builder, as the text field cell set there takes precedence.

Have you tried setCell: method of the NSControl class?
- (void)setCell:(NSCell *)aCell

Related

Subclassing NSTextView makes undo impossible in Cocoa

I have a very simple Cocoa program. One window, with an NSTextView inside of it. In this configuration, the NSTextView operates exactly how you would expect it to. I can type, undo, redo, everything.
But then I subclass NSTextView, lets call it TextView. This new class does not override any methods, it's basically a blank subclass. I replace the NSTextView with my new subclass of it. I can type text into the TextView, but undo does not work. It just beeps at me. The undo menu item is greyed out. I would not expect this to happen, since TextView doesn't add any new code to the object inheritance structure.
What must I do to my TextView to re-enable undo?
GitHub project
XIB object hierarchy, "Text View" is my "TextView" class
Edit: upon further observation, there indeed was an edit in my subclass that caused the issue, as Mohamad Farhand opined.
overriding method : shouldChangeTextInRange cause this issue
i recommend you to check this link :
Cocoa: looking for a general strategy for programmatic manipulation of NSTextView storage without messing up undo
You may need to enable allowsUndo property of your subclassed TextView class.
#implementation TextView
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
self.allowsUndo = YES;
}
return self;
}
#end

How do you access the outlets of the main view controller from a logic class?

I have my viewcontroller class for my only current View and another class with static methods for my mathematical logic. ViewController class has an IBOutlet for a label. How can I reference this outlet from within the functions of my Logic class?
You could pass a pointer to the logic class just like any other variable, but I wouldn't recommend directly accessing the IBOutlet property.
What I'd recommend, is either having the logic class return the values and have the controller update the label as needed, or if it involves background processing that doesn't return immediately, use the delegate pattern. This way, the logic class will inform the controller when the data is ready, or the calculations are finished, and then the controller can update the UI as needed.
Look into iOS Protocols to define the structure of a delegate class :)
You shouldn't allow your Logic class to access a UI control because it does not follow the Model-View-Controller pattern, which is a smart way to keep your code organized so that it's easier to follow as your project becomes more complicated. Instead, you would want your ViewController to communicate between the UI and the Logic class for you.
For example, if you had a Calculate button at the bottom of your view that the user taps, the tap should be handled by the ViewController. The ViewController would call a function in your Logic class that might return a value. Then the ViewController would take that value and set it as the text of the label. Here's a snippet of code that illustrates the idea:
- (IBAction) calculateSomeValue: (id) sender {
int result = [Logic calculateValue];
[self.label setText: [NSString stringWithFormat: #"Your result is: %d", result]];
}

How to access objects from different classes in Cocoa Programming

I have an NSTextField subclass (called "txtField1" and used as Custom Class for a Text Field in my interface builder) and I would like to be able to access an NSComboBox object which present in my interface builder from this class.
This is my code:
txtField1.h:
#import <Cocoa/Cocoa.h>
#interface txtField1 : NSTextField
#end
txtField.m:
#import "txtField1.h"
#implementation txtField1
-(void)mouseDown:(NSEvent *)theEvent
{
HERE I would like to be able to write something like:
[combobox SetHidden:YES];
}
#end
I would like to be able to set access the combobox SetHidden property, in the mouseDown event.
Can you please tell me how to do that? I have tried different solutions found on internet but didn't obtain anything at all!
Any help would be appreciated.
Here are a lot of ways, and answers here, to do :
Update a label through button from different view
Xcode - update ViewController label text from different view
Setting label text in another class
Set label on another view to stored NSDate
EDIT:
-(void)mouseDown:(NSEvent *)theEvent
{
HERE I would like to be able to write something like:
[combobox SetHidden:YES];
/*
use the shared instance of comboBox here and make it hidden.
Also, you can use binding to make it hidden
*/
}
From my point of view txtField1 class is not better place to this code.
You can add NSControlTextEditingDelegate protocol to your NSViewController implementation (that already contains IBOutlets for txtField1 and combobox) and in method – control:textView:doCommandBySelector: implement hiding of your NSComboBox

Custom NSButton and NSButtonCell

I'm trying to create a custom button look.
From what I've gathered, NSButtonCell does the drawing, so I should actually be overwriting that instead.
But the issue is, my CustomButton class has other things like NSImage, mIsMouseOver etc. Currently the drawing is being done in the CustomButton class but I want to move it over to the cell.
question is, is there anyway I can access the image in the customButton class from the customButtonCell class so that I may use [image drawInRect:...]?
Regards,
Han
Your cell's drawWithFrame:(NSRect)frame inView:(NSView *)controlView method includes a reference to the NSView being drawn from which you can access the view's properties (such as image).
Usual way is to store the data in the NSCell subclass. Base cell class even has an -(id)image property, so, your button class should call [[self cell] image] when it is queried for image.
Actually, since you are subclassing NSButton, it contains all you need, just override cell's drawing methods. And if you need an extra property - define it in the cell, wrap in the control.

refreshing mkannotation properties and refreshing the annotation

I have a mkannotation located on a mapview which has a mkannotationview as well as a calloutview which when clicked goes to a child uiviewcontroller. I am updating some properties from the callout's uiviewcontroller, but after I'm finished I want to move the annotation's position on the map and change the annotation title and subtitle. How can I easily do this from the callout's uiviewcontoller? What's the most elegant way to handle this? A code sample would be great if anyone has any.
Thanks
I'd create a protocol, say MapCallBackDelegate, to handle what you want to do. This avoids tightly coupled code. Put this in your map annotation view header file
#protocol MapCallBackDelegate
-(void)updateAnnotation:(id)whatEverParamsYouWant;
#end
Then make your Map View implement this protocol. When you create your map annotation view, give it a property
#property (nonatomic, retain) id<MapCallBackDelegate> callbackDelegate;
And when you add it to your map, set that property to self
myMapAnnotationView.callbackDelegate = self;
so when you want to change the title/subtitle/position, you just invoke that message on the callbkacDelegate.
This is elegant because it reduces tightly-coupled code, allows other objects to implement the same protocol for code reuse later, and promotes information hiding in your MapAnnotationView.
Remove the annotation from the map entirely, update it, and add it to the map again. That'll ensure that the map notices that the annotations location has changed.
Although you can remove and add the annotation back as #Caleb suggests, another option is to update the coordinate property directly on the annotation you want to move.
Note that this will only work if your annotation class implements setCoordinate which can easily be done by declaring the coordinate as assign (like the built-in MKPointAnnotation class does) instead of readonly. The map view will see the change via KVO and move the annotation.
To have the child view controller tell the map view controller which annotation to change and what the new coordinates are, I recommend using delegate+protocol as another answer suggests.
The easiest way would to actually not do it from the child view controller. Maybe your needs are different from what I understand from the question, but at first blush I would do something like this:
In the header:
#interface YourController
{
...
MKAnnotation *_latestDetailViewed;
}
...
#property(nonatomic, retain) MKAnnotation *latestDetailViewed;
#end
Then in the .m something like
#implementation YourController
...
#synthesize latestDetailViewed = _latestDetailViewed;
...
-(void) dealloc
{
...
self.latestDetailViewed = nil;
[super dealloc];
}
-(void) whereverYouLaunchYourDetailScreenFrom:(MKAnnotation*)detailAnnotation
{
self.latestDetailViewed = detailAnnotation;
// then create/push your view controller
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(_latestDetailViewed)
{
// Do whatever you want to the annotation here
self.latestDetailViewed = nil;
}
}
That way your changes will be made when you come back to the map. If you're really only launching a detail view for one annotation at a time, and always coming back to the map in between, then it should work without making you deal with writing a delegate protocol or firing off NSNotifications.
If I'm misunderstanding your situation let me know and I'll give you a different answer :)