Required Methods for Text Changes in Custom NSTextView - objective-c

I have a custom NSTextView, and I want to be sure to send all of the proper messages and notifications when making changes to the text programmatically. I mostly care about undo/redo registration, but I'd generally like to do things the "right" way. Anyway …
The messages I'm aware of are:
-shouldChangeTextInRange:replacementString:
-didChangeText
If I understand the documentation correctly, before any changes are made to the text view's textStorage object, one needs to send the -shouldChangeTextInRange:replacementString: message to make sure a new undo group is opened (and inform any delegates of the commencement of editing). If YES is returned, the changes can be made to the text. Once all of the changes have been applied, the -didChangeText message needs to be sent to close the undo group (and again, notify any observers). Is this right?
When modifying existing characters (or attributes) those instructions make sense to me. I'm acting on an existing range of text, so it's easy to know what to send for the affectedCharRange parameter. What about when I need to insert something?
Let's say I want to insert a random word at the current insertion point index. Do I need to send the -shouldChangeTextInRange:replacementString: message? I'm not modifying existing characters, but I am adding new characters to the existing characters.
If I do need to send this message, what range do I use for the affectedCharRange argument? Whenever I try to send the new computed range of the to-be-inserted text, I get "Range Out of Bounds" errors, which makes sense, considering the text view's length has yet to change. Do I just send the range for the insertion point with an empty length (e.g. self.selectedRange when nothing's selected)?
For example:
- (void)insertRandomWord:(id)sender
{
NSAttributedString *randomAttrStr = [self randomAttributedString];
BOOL shouldChangeText = [self shouldChangeTextInRange:shouldThisBeTheSelectedRange // <-- WTF, mate?
replacementString:randomAttrStr.string];
if ( shouldChangeText ) {
[self.textStorage insertAttributedString:randomAttrStr
atIndex:self.selectedRange.location];
// This should always get called, right?
[self didChangeText];
// Is this where I would set the typing attributes?
self.typingAttributes = [randomAttrStr attributesAtIndex:0 effectiveRange:NULL];
}
}
I've gone as far as to create a method that takes a range and a block as arguments, so I don't accidentally forget to call something. Is this a good idea or not?
- (void)changeTextInRange:(NSRange)range replacementString:(NSString *)replacementString usingBlock:(void (^)(void))block
{
BOOL change = [self shouldChangeTextInRange:range replacementString:replacementString];
if ( change ) {
block();
[self didChangeText];
}
}
Last, but not least, do I need to call the NSTextStorage "editing" methods, too? The methods I'm referring to are:
-beginEditing:
-endEditing:
The documentation discusses calling those when one subclasses NSTextStorage, but I'm a little confused as to whether those messages need to be sent, too. It doesn't really say whether or not to use -fixAttributesInRange:, but I know the -endEditing message calls that to do cleanup after an editing operation.
I apologize for the discombobulated mess of questions. I'm just super tired and confused, and Apple's documentation has been … lacking. Any tips, pointers, and/or suggestions would be greatly appreciated. Thanks!

Related

Override setter in Objective-c. Why check is equal parameter

Often find custom setter sintaxis like this:
- (void)setParentCatalog:(Catalog *)parentCatalog {
if (_parentCatalog != parentCatalog) { //???
_parentCatalog = parentCatalog;
[self refresh];
}
}
Why i should check?
if (_parentCatalog != parentCatalog)
This checks if both _parentCatalog and parentCatalog are pointing to the same memory location.
If both are same object then no need to set the objectValue.
The reason for checking if the two are equal is to avoid executing code when it's not necessary. If the method is called very often, this could have a performance benefit. Under non-ARC, your code might look more like this:
- (void)setParentCatalog:(Catalog *)parentCatalog {
if (_parentCatalog != parentCatalog) {
[_parentCatalog release];
[parentCatalog retain];
_parentCatalog = parentCatalog;
[self refresh];
}
}
So, by checking that what you received is actually a new value, you avoid those retain and release calls happening (which are still there with ARC). You've also got [self refresh] in there, which probably doesn't need to happen unless the value has actually changed.
The idea here is that if the parameter passed in to the setter is the same object already stored in the property, then there is no need to call [self refresh] again.
A refresh method often reads in data, works on it and then re-displays it in the app's views. No need to do all this work again if the data in the property haven't really changed.
It's a decision that is use case dependant. The idea behind this guard is to prevent doing unnecessary work.
If you imagine that your [self refresh] kicked off a very expensive operation then you would be reluctant to do it every time. So if you only do it when the object actually changes you save yourself some work.
Of course this may well be the behaviour you are looking for in which case you would need to stick the [self refresh] call outside of the guard.
Like all code examples you find it's worth weighing up the trade offs of the implementation and then you can better decide what you need in your case.

understanding whether to use protocol or inheritance (or both?)

I have a type of object in my game that simply moves horizontally.. It has a method like this:
-(void)moveLeftWithBlock:(void(^)())block {
self.targetX = self.position.x - MOVE_AMOUNT;
id action = [CCMoveTo actionWithDuration:0.125f position:ccp(self.targetX, self.targetY)];
id ease = [CCEaseInOut actionWithAction:action rate:4];
[self runAction: [CCSequence actions: ease,
[CCCallBlock actionWithBlock:block], nil]];
}
...
Now, I wanted to make a second type of object that's exactly the same, except that it can also move vertically... My initial thought was to just subclass that object's class, and overwrite the moveLeftWithBlock and update its targetY.. but not only do I not like that solution because I still end up with 99% duplicate code amongst the two classes, but also because my game needs the Y position set prior to that moveLeftWithBlock method being called.
So next I thought-- Ok, so I can make my game call a prepareToMove method, which could do any kind of setup that might be required... For the main object type, nothing.. For this 2nd object type, set the targetY.
However, I immediately started thinking-- wait---- I know protocols are about explicitly defining methods that are OPTIONAL or REQUIRED, and it made me think that perhaps I should be utilizing this here, but I just am not sure how.
So, can anyone explain to me how a protocol could be used in this case-- and if it is "the way to go" or not?
Any of these would work. You could add to this andAlsoMoveVertically: (bool) isVertical and then add the vertical movement in an if statement in the function.

How to bind a buttons title to a particular value in an array IOS?

I have six on-screen buttons whose titles need to correspond to six elements in an NSMutableArray, when the value in the array changes, I also need the title to change with it. I am having trouble figuring out how to create that constantly updating line to the button, I'm still quiet new to objective-c development as well as Xcode.
I also need to make sure that when there is no value at that particular index of the array, the button cannot be clicked on
here is an example of one of the buttons
- (IBAction)card1Pressed:(id)sender {
if (self.userHasEnteredFirstNumber) {
if (!self.userHasEnteredSecondNumber) {
self.secondNumber = [sender currentTitle];
}
}
else{
self.firstNumber = [sender currentTitle];
}
}
The end goal is to have the user press two buttons, then chose weather to add, subtract, multiply, or divide them. After they pick one of those four operations, the values that the buttons were assigned to in the array will be removed and replaced with whatever the new number is. So after they do this once there will only be 5 numbers left in the array, then 4, then 3..... and so on.
The numbers will be drawn and added to an NSMutable array titled currentHand
UPDATE: Using UIOutletCollection I linked the buttons to the method like this
the link to the picture is here "sorry about not being able to directly post it but new users must have a reputation of 10 before they can"
link to photo of declaration and implementation with interface-builder of IBOutletCollection
was this correct?
the code for the header file regarding the IBOutletCollection is as follows "please note that this has been connected to the six buttons I want to use it with in interface builder, a picture of it is shown above"
#property (nonatomic,retain) IBOutletCollection (UIButton)NSArray *buttonArray;
the code in the implementation file regarding the IBOutletController is as follows
# synthesize buttonArray = _buttonArray;
You want to use Key-Value observing, check out this from the Apple documentation:
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i
Okay okay, so essentially you have a 'Button'-Array, whose 'Caption' is served by a NSMutableArray, which can easily be changed around, correct?
Now, this is only how I'd do it, there may be better solutions out there, but here goes:
You need a 'change' interface, which is called EACH time, something happens to your NSMutableArray. For example (since I don't know WHAT exactly is in that array of yours):
- (void) ChangeArray:(int)IDofElement (NSString *)newValue {
[arr replaceObjectAtIndex:IDofElement withObject:newValue]; //Updates/Empties the indexed Element.
if(newValue != nil) {
[buttons[IDOfElement] setText:newValue];
}
buttons[IDOfElement].enabled = (newValue != nil); //Makes Button 'clickable'
}
Like this, each time the value inside that array of yours gets changed, the corresponding caption also gets updated. Of course you would need an array of the buttons for this to work, but I don't think that's the big problem.
Another possible solution: Save a reference to the button inside of the object that's stored in the array and whenever the value is changed, change the button's caption too.
Does this help? Mind you, this is not the 'perfect solution', but something I'd come up with.
Please also note, that this is just from the top of my head and probably faulty at some points. But it should be able to point you in the general direction.

Use of pass by reference in Objective-C

Looking at the JSON-Framework source, it makes heavy use of pass by reference in many of the parser method signatures. i.e.
#interface SBJsonParser ()
- (BOOL)scanValue:(NSObject **)o;
- (BOOL)scanRestOfArray:(NSMutableArray **)o;
- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o;
#end
This ends up being used something like this:
id o;
[self scanValue:&o];
// Do something with o
- (BOOL)scanValue:(NSObject **)o {
// Cut down for brevity
return [self scanRestOfDictionary:(NSMutableDictionary **)o];
}
- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o {
// Cut down for brevity
*o = [NSMutableDictionary dictionaryWithCapacity:7];
[*o setObject:#"value" forKey:#"key"];
return YES;
}
What are the benefits to this approach?
EDIT: I'm asking more from a design point of view. I understand what pass by reference is, I'm just wondering when it's appropriate to use it. The design used in SBJsonParser is similar to the API used in NSScanner:
- (BOOL)scanUpToString:(NSString *)stopString intoString:(NSString **)stringValue;
To me, this implies that the string which was scanned is secondary to needing to know if something was scanned. This is in contrast to the API used by NSString:
+ (id)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error;
In that API, the contents of the file is the primary concern, and the NSError reference is used to pass back an error in the event that something goes wrong.
Just after some general thoughts on which API is most appropriate, when.
Those are "output" parameters. They allow the called method to assign a value to your local variable "o". In other words, you're not passing in a reference to an object, but a reference to a local variable.
In your case, the methods return a BOOL to indicate success or failure; therefore, they use output parameters to return other values and objects.
It's really just a style question. It should be consistent across an entire API.
On the one hand, you've got a style where the status code of the call is always returned and output of the call is in the parameter list.
Benefits? You can always check the call result for success. You can easily have multiple return values without changing the style.
Drawbacks? Can't just drop in calls in place of parameters. Harder to chain.
On the other hand, you've got a style where the primary data is returned from the call and any error codes are done through out parameters.
The benefits and drawbacks are essentially inverted.
To be fair, there's a third style: no results are passed out or returned. Instead, exceptions are used.
Benefits? Cleaner looking code.
Drawbacks? Works well for errors, but not so well for status codes that may go along with valid return codes.

How do I set Cocoa NSLevelIndicatorCell values?

I have a tableview. One of the columns in the tableview uses an NSLevelIndicatorCell.
I want to be able to allow the user to edit the warn and critical values for the level indicator such that when they enter a value into a a "warning level" textbox, it changes the warn value of the level indicators being displayed in ALL of the tableview's rows.
I am very much a newbie with Objective-C so all I can figure out so far is that I must need a delegate method to watch the textbox BUT if I succeed in doing that, how on earth do I send the new value to the particular tableview column so that the update happens to ALL of the rows (i.e. how do I send what message to the tableview and target a cell within a column within a tableview)?
Here is the code to the solution I came up with should anyone need it.
- (IBAction)setWarningLevel:(id)sender {
double v;
NSScanner *ns = [NSScanner scannerWithString:[warnLevel stringValue]];
[ns scanDouble:&v];
[levelIndicator setWarningValue:v];
}
This is a textbook case for using Cocoa bindings. Just bind the value of the text field to the NSLevelIndicatorCell in your table view (do that in Interface Builder). The updates should happen automagically.
I think it should apply for all the cells in the table view if you apply the binding to the cell in IB. However if it doesn't, you will need to write a couple lines of code that set up the binding every time a new row in the table is created. That link above will explain everything in detail, but basically you will be setting up a Key-Value Observer relationship in code between the text field and the instance of the level indicator in the row being created.
I think you may have overdone it.
NSTextField subclasses NSControl, so you need to look in the docs for NSControl for a useful function.
Try re-writing it like this; assuming you're taking the value from a warnLevel textfield.
- (IBAction)setWarningLevel:(id)sender {
double v = [warnLevel doubleValue];
[levelIndicator setWarningValue:v];
}
Although this is usually shortened to this;
- (IBAction)setWarningLevel:(id)sender {
[levelIndicator setWarningValue:[warnLevel doubleValue]];
}
You should probably have some validation that the textfield has a valid number. If you're only choosing a couple of numbers have a look at using a stepper control.
Usually, with Cocoa, if you feel like you're jumping through too many hoops, there is sometimes an easier way.
Usually ;-)