How do I change the shape of UITextView in Xcode 5? - xcode

Instead of having a normal text view, I want it to be another shape, for instance like the Text Field. The reason to why I am not using Text Field is because I want the text view to be uneditable.
Suggestions on how to change the shape?
Thanks.

If what you really want to do is use a UITextField you have a few options:
textField.enabled = NO; // create an outlet to your text field and put this in ViewDidLoad to prevent editing but also selecting and copying
implement the delegate method: // this will allow copy/paste, etc. but no writing
(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
return NO; }
If you want to use a UITextView, the only shape items you can change are the width and height (you can't make it star shaped or anything). If you want rounded corners you could use QuartzCore as follows:
[textView.layer setBorderWidth:2.0]; // not needed but put here in case
//Round the corners via a radius value`enter code here` (play with number to get what you want)
textView.layer.cornerRadius = 5;
textView.clipsToBounds = YES;
Note: UITextView gives you the added benefit of multi-lines and scrolling.
If you just want to display a value that can be read and copied then use a UILabel

Related

Xcode How do you make circular hitboxes on buttons?

I'm using Xcode 5 to make an UIButton. I made the button in storyboard and set the background image to a .png of a circle with transparency. I set up a simple action for the button to display how many times it was pressed in a label.
When I press the corners of the circle on the screen, it still adds to the score. So the button is still keeping its square hitbox, even though it has a round image as its background. I searched everywhere for a way to make a circular hitbox, but I can't find anything. Is this even possible? Is there an alternative way to do this?
The button responds to touch on corners because the bounding box of the button is a rectangle no matter what image you set for background.
To achieve a circular UIButton you must make a circular bounding box by simply using this code:
- (UIView *)setRoundedView:(UIView *)roundedView toDiameter:(float)newSize {
CGPoint saveCenter = roundedView.center;
CGRect newFrame = CGRectMake(roundedView.frame.origin.x, roundedView.frame.origin.y, newSize, newSize);
roundedView.frame = newFrame;
roundedView.layer.cornerRadius = newSize / 2.0;
roundedView.center = saveCenter;
roundedView.layer.masksToBounds = YES;
return roundedView;
}
In this method you can pass any UIView or subclass of UIView in your case UIButton. So in code you must create an IBOutlet of you button and in your -viewDidLoad method simply put this line of code:
self.btnMyButton = (UIButton *)[self setRoundedView:self.profilePic toDiameter:34];
Assuming you UIButton outlet is btnMyButton and set the diameter as per your requirement.
Hope this helps.
Don't forget to import QuartzCore Framework.

Change NSTextField's behavior for multiple clicks in a row

I have a NSTextField which is nested by a custom view and I want to change the default behavior of multiple clicks in a row (double click, tripple click etc.), similarly to the behavior of text nodes MindNode (see the image below).
I want the first click to "activate" the text field and then go on from the beginning (like reseting the click count of the event).
I have following ideas, but I don't know how to implement them and if they actually make sense:
Somehow change the time using +[NSEvent doubleClickInterval] and slow down the second click.
Reduce the click count programmatically?
Make the NSTextField non-selectable using -hitTest:, forward the click to the superview, change some parameter of the text field and accept the next clicks. In this case, the click count of the second click is still 2.
Override -mouseDown: and not call super. This breaks the NSTextField's selection functionality.
I hope there is an easier way to achieve this, which I have overlooked.
Thanks for your answers!
Here is a graphical representation of the problem:
I would do this by embedding the text field and a custom view in an NSBox, which would be set to the custom type, initially with no background color or border (so it would be invisible). Initially, the custom view would be on top and it would have a mouseDown: method that would receive the first click. In that method you could rearrange the box's subviews so that the text field would then be on top and receive the next clicks. If you wanted, the box could be somewhat bigger than the text field so you could give it a background color or other drawing that would look like a custom activation ring around the text field. In the text field's controlTextDidEndEditing: method, you could reset the system back to the beginning state, so it would be ready for the next time you click on it.
After Edit: Here is the code I'm using in my overlay class:
#implementation Overlay
static NSComparisonResult rdComparator( NSView *view1, NSView *view2, void *context ) {
if ([view1 isKindOfClass:[NSTextField class]])
return NSOrderedDescending;
else if ([view2 isKindOfClass:[NSTextField class]])
return NSOrderedAscending;
return NSOrderedSame;
}
-(void)mouseDown:(NSEvent *)theEvent {
self.box.fillColor = [NSColor redColor];
NSView *contentView = self.box.subviews.lastObject;
[contentView sortSubviewsUsingFunction:rdComparator context:nil];
}
I've solved it by subclassing NSTextField and decrementing click count of mouse down events programmatically. Using a boolean property of the subclass I am able to turn this special behavior on and off.
- (void)mouseDown:(NSEvent *)theEvent
{
if (self.specialBehavior) {
theEvent = [NSEvent mouseEventWithType:theEvent.type
location:theEvent.locationInWindow
modifierFlags:theEvent.modifierFlags
timestamp:theEvent.timestamp
windowNumber:theEvent.windowNumber
context:theEvent.context
eventNumber:theEvent.eventNumber
clickCount:theEvent.clickCount - 1
pressure:theEvent.pressure];
}
[super mouseDown:theEvent];
}
To simplify this long method call, I wrote a category method for NSEvent which decrements the click count of an event.

NSSplitView resizes the custom NSView contained

I've a vertical NSSplitView, the bottom subview contains a custom view (eg NSView) and a NSTextView.
The NSView contains inside it two NSButtons.
When I resize the splitView, making it smaller, the NSView containing the buttons is resized, too.
I don't want this behavior.
To better explain my problem please view the attached image.
Image 1: the window at application startup, everything is ok
Image 2: I've resized making smaller the split view, only a little part of buttons is visible
Image 3: I've enlarged again the split view but as you can see the NSView remains smaller and buttons are no longer visible (if I resize the splitView to bottom the NSView 'disappears')
This is a vicious problem that's based on the legacy workings of Cocoa views. The best solution I've seen is to constrain the minimum dimension of any portion of the split view. If the subviews never collapse, their metrics don't cross into another dimension and they should re-enlarge just fine.
To do this, set up a delegate for your split view, which will implement - splitView:constrainMaxCoordinate:ofSubviewAt:. The split view will call your delegate method hoping it can leave the max divider position at the height of the split view (passing this in as the second argument), but you can simply subtract some quantity from that value (say, 60) to return it as the minimum height for the bottom view.
- (CGFloat)splitView:(NSSplitView *)aSplitView
constrainMaxCoordinate:(CGFloat)proposedMin
ofSubviewAt:(NSInteger)dividerIndex {
return proposedMin - 60;
}
Of course, you'll probably want to do more checking in this method to make sure you're talking about the right split view, and the right subview, to avoid overreaching effects, but this is the basic idea.
(See also this fabulicious article on the subject.)
Constraining the divider position did not help in my case, as I'm animating the subviews and subviews can be collapsed.
I managed to achieve an acceptable solution by implementing the splitView delegate method -splitviewWillResizeSubviews: (means, you have to connect the delegate property from the split view to your controller in IB or in code) to maintain a minimum width by setting the subview to hidden instead of shrinking it to zero:
- (void)splitViewWillResizeSubviews:(NSNotification *)notification {
NSUInteger divider = [[[notification userInfo] valueForKey:#"NSSplitViewDividerIndex"] intValue];
NSView *subview = nil;
if(divider == SPLITVIEW_DIVIDER_SIDEBAR) {
subview = (NSView*)[self.splitView.subviews objectAtIndex:SPLITVIEW_SIDEBAR_INDEX];
}
if(subview) {
if(subview.frame.size.width < SPLITVIEW_MINIMUM_SIDEBAR_WIDTH) {
CGRect correctedFrame = subview.frame;
correctedFrame.size.width = SPLITVIEW_MINIMUM_SIDEBAR_WIDTH;
subview.frame = correctedFrame;
subview.hidden = YES;
} else {
subview.hidden = NO;
}
}
}

Automatically resize an NSButton to fit programmatically changed text (Xcode)

I have an NSButton (Push Button) with some temporary title text built in Interface Builder / Xcode. Elsewhere, the title text inside the button is changed programmatically to a string of unknown length (actually, many times to many different lengths).
I'd like the button to automatically be resized (with a fixed right position--so it grows out to the left) to fit whatever length of string is programmatically inserted as button text. But I can't figure it out. Any suggestions? Thanks in advance!
If you can't use Auto Layout as suggested by #jtbandes (it's only available in Lion), then you can call [button sizeToFit] after setting its string value, which will make the button resize to fit its string. You would then need to adjust its frame based on the new width.
You can't do this automatically, but it would be easy to do in a subclass of NSButton.
#implementation RKSizeToFitButton
- (void)setStringValue:(NSString*)aString
{
//get the current frame
NSRect frame = [self frame];
//button label
[super setStringValue:aString];
//resize to fit the new string
[self sizeToFit];
//calculate the difference between the two frame widths
NSSize newSize = self.frame.size;
CGFloat widthDelta = newSize.width - NSWidth(frame);
//set the frame origin
[self setFrameOrigin:NSMakePoint(NSMinX(self.frame) - widthDelta, NSMinY(self.frame))];
}
#end
This way you can just set your button's class to RKSizeToFitButton in Interface Builder and then calling setStringValue: on the button to change its label will "just work" with no additional code.
Sure! Just use Auto Layout! :)

Cocoa Text - refreshing text on-the-fly

In an app I'm working on, the user inputs plain text, and the app reformats the text by transforming it to an NSAttributedString, and displays it. This all happens live.
Currently, I'm doing the following on my NSTextView's textDidChange delegate method:
- (void)textDidChange:(NSNotification *)notification {
// saving the cursor position
NSInteger insertionPoint = [[[self.mainTextView selectedRanges] objectAtIndex:0] rangeValue].location;
// this grabs the text view's contact as plain text
[self updateContentFromTextView];
// this creates an attributed strings and displays it
[self updateTextViewFromContent];
// resetting the cursor position
self.mainTextView.selectedRange = NSMakeRange(insertionPoint, 0);
}
While this mostly works, it's not ideal. The text seems to blink for a split second (you especially notice it on the red dots under spelling errors), and when the cursor was previously near one of the edges of the visible rect, it the scroll position gets reset. In my case, this is a very much undesirable side-effect.
So my question is: Is there a better way of doing what I'm trying to do?
I think you have a slight misconception of how an NSTextView works. The user never enters a "plain string", the data store of an NSTextView is always an NSTextStorage object, which is a subclass of NSMutableAttributedString.
What you need to do is add/remove attributes to the existing attributed string that the user is editing, rather than replacing the entire string.
You should also not make changes to the string in the ‑textDidChange: delegate method, as changing the string from that method can cause another change notification.
Instead, you should implement the delegate method ‑textStorageDidProcessEditing:. This is called whenever the text changes. You can then make modifications to the string like so:
- (void)textStorageDidProcessEditing:(NSNotification*)notification
{
//get the text storage object from the notification
NSTextStorage* textStorage = [notification object];
//get the range of the entire run of text
NSRange aRange = NSMakeRange(0, [textStorage length]);
//for example purposes, change all the text to yellow
//remove existing coloring
[textStorage removeAttribute:NSForegroundColorAttributeName range:aRange];
//add new coloring
[textStorage addAttribute:NSForegroundColorAttributeName
value:[NSColor yellowColor]
range:aRange];
}

Resources