how to make NSTextField to fit NSMutableAttributedString contained - objective-c

I have a NSTextField where I add NSMutableAttributedString. I want to set the size of that string to big number, however when I do that the text appears cut off. How can tell the NSTextField to get bigger?
This is what I have:
NSTextField* textField = [[NSTextField alloc] init];
NSMutableAttributedString* text = [[NSMutableAttributedString alloc]
initWithString:#"0"];
NSRange titleRange = NSMakeRange(0, [text length]);
[text addAttribute:NSFontAttributeName
value:[NSFont boldSystemFontOfSize:25]
range:titleRange];
[textField setAttributedStringValue:text];
Any advice?
Thanks in advance

Make a subclass of NSTextField
In implementation do override intrinsicContentSize
-(NSSize)intrinsicContentSize
{
if ( ![self.cell wraps] ) {
return [super intrinsicContentSize];
}
NSRect frame = [self frame];
CGFloat width = frame.size.width;
frame.size.height = CGFLOAT_MAX;
CGFloat height = [self.cell cellSizeForBounds: frame].height;
return NSMakeSize(width, height);
}
// Than invalidate the layout when text changes
- (void)textDidChange:(NSNotification *)notification
{
[super textDidChange:notification];
[self invalidateIntrinsicContentSize];
}
In attribute Inspector inside storyboard set NSTextField class to your customNSTextField class and change layout to wraps from scrolls.
You must add constraints to your textField to its superview, easier in storyboard.
After that you can also set font size directly :
[_textField setFont:[NSFont systemFontOfSize:25]];

[textField sizeToFit];
But using autolayout, is usually a better idea.

Related

CATextLayer number of lines?

My CATextlayer support only 1 line
otherwise the text is cut.
trying to set text content like UILabel Behaviour... is it possible?
set "number of lines"
adjust text size by static CATextLayer frame
CATextLayer *text_layer= [[CATextLayer alloc] init];
[text_layer setBackgroundColor:[UIColor clearColor].CGColor];
[text_layer setBackgroundColor:[UIColor blueColor].CGColor];
[text_layer setForegroundColor:layers.textColor.CGColor];
[text_layer setAlignmentMode:kCAAlignmentCenter];
[text_layer setBorderColor:layers.borderColor.CGColor];
[text_layer setFrame:CGRectMake(0,0,200,50)]; //note: frame must be static
[text_layer setString:#"thank you for your respond"];
text_layer.wrapped = YES;
[text_layer setAlignmentMode:kCAAlignmentCenter];
Your problem is this line right here, [text_layer setFrame:CGRectMake(0,0,200,50)];. I don't think a CATextLayer would lay itself out to accommodate multiple lines. It will only re-draw the text to wrap within the layer's bounds. Try adjusting your text layer's frame based on the text being set. You can create a UILabel instance, to calculate the frame for having multiline text with word wrapping and set it to your CATextLayer instance.
Here's a UILabel category to calculate text size for multiline text with word wrapping:
#interface UILabel (Height)
- (CGSize)sizeForWrappedText;
#end
#implementation UILabel (Height)
- (CGSize)sizeForWrappedText {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.bounds.size.width, CGFLOAT_MAX)];
label.numberOfLines = 0;
label.font = self.font;
label.text = self.text;
[label sizeToFit];
return label.frame.size;
}
#end
Create a UILabel instance, and use the sizeForWrappedText to get the size. Something like this:
// Make sure the someFrame here has the preferred width you want for your text_layer instance.
UILabel *label = [[UILabel alloc] initWithFrame:someFrame];
[label setText:#"My awesome text!"];
CGRect frame = text_layer.frame;
frame.size = [label sizeForWrappedText];
[text_layer setFrame:frame];

Showing NSAttributedString at the Center With NSView

I have the following function to make an attributed string.
- (NSMutableAttributedString *)makeText:(NSString *)txt : (NSString *)fontname : (CGFloat)tsize :(NSColor *)textColor :(NSColor *)shadowColor :(NSSize)offset {
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = shadowColor;
shadow.shadowOffset = offset;
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 10.0f;
paragraphStyle.alignment = NSCenterTextAlignment;
NSMutableAttributedString *atext = [[NSMutableAttributedString alloc] initWithString:txt];
NSRange range = NSMakeRange(0,txt.length);
// alignment
[atext addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0,[atext length])];
...
...
return atext;
}
If I set this attributed string to NSTextField, the resulting attributed string will be aligned correctly. But if I send it to NSView's subclass, the string will be left-aligned. Is there any way by which I can display this attributed string with correct alignment?
// .h
#interface imageView1 : NSView {
NSAttributedString *aStr;
}
// .m
- (void)drawRect:(NSRect)dirtyRect {
[aStr drawAtPoint:NSMakePoint((self.frame.size.width-aStr.size.width)/2.0f,(self.frame.size.height-aStr.size.height)/2.0f)];
}
Thank you for your help. The OS version is 10.8.
If you draw at a point there are no bounds in relation to which to center. You need to specify a rect. See the docs on drawWithRect:options:. Note the admonition there pointing out that to be in that rect your options will need to be (or include) NSStringDrawingUsesLineFragmentOrigin.

Adding NSMatrix of buttons (NSRadioModeMatrix) in custom view, setting target/action

I am subclassing NSTabView to customise the appearance. I want to use an NSMatrix of NSButtonCells to select the tabs. I managed to add the NSMatrix with buttons in the initWithFrame: method of my NSTabView subclass. What I can not get to work is setting the target and action programmatically. Here is what I tried:
define TAB_WIDTH 24.0f
define TAB_HEIGHT 24.0f
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
if (self) {
NSInteger numberOfTabs = 5;
NSInteger tabSpacing = 8;
NSSize cellSize = NSMakeSize(TAB_WIDTH, TAB_HEIGHT);
NSSize interCellSpacing = NSMakeSize(tabSpacing, 0);
CGFloat tabSelectorWidth = TAB_WIDTH * numberOfTabs + tabSpacing * numberOfTabs - 1;
CGFloat xOrigin = (frameRect.size.width - tabSelectorWidth) / 2;
NSRect tabSelectorFrame = NSMakeRect(xOrigin, 0, tabSelectorWidth, TAB_HEIGHT);
NSButtonCell *cellPrototype = [[NSButtonCell alloc] init];
[cellPrototype setBordered:NO];
_tabSelector = [[NSMatrix alloc] initWithFrame:tabSelectorFrame
mode:NSRadioModeMatrix
prototype:cellPrototype
numberOfRows:1
numberOfColumns:5];
[_tabSelector setTarget:self];
[_tabSelector setAction:#selector(selectedTab)];
[_tabSelector setCellSize:cellSize];
[_tabSelector setIntercellSpacing:interCellSpacing];
NSArray *theCells = [_tabSelector cells];
[theCells[0] setImage:[NSImage imageNamed:#"tab1"]];
[theCells[1] setImage:[NSImage imageNamed:#"tab2"]];
[theCells[2] setImage:[NSImage imageNamed:#"tab3"]];
[theCells[3] setImage:[NSImage imageNamed:#"tab4"]];
[theCells[4] setImage:[NSImage imageNamed:#"tab5"]];
[self addSubview:_tabSelector];
[self setDrawsBackground:NO];
[self setTabViewType:NSNoTabsNoBorder];
}
return self;
}
- (void)selectTab:(NSMatrix *)sender
{
NSLog(#"selected tab");
}
The view is drawn as desired, but clicking the buttons does not call the target method.
I have tried to add buttons programmatically to a standard IB view as described here Programatically create and position an NSButton in an OS X app?
That works, but things fall apart in my custom view. Can anybody give me a hint what I am missing?
Martin
Try changing
` [_tabSelector setAction:#selector(selectedTab)];`
To
` [_tabSelector setAction:#selector(selectedTab:)];`

Render Title of MKPolygon

I'm trying to render MKPolygon using the following code:
NSMutableArray *overlays = [NSMutableArray array];
for (NSDictionary *state in states) {
NSArray *points = [state valueForKeyPath:#"point"];
NSInteger numberOfCoordinates = [points count];
CLLocationCoordinate2D *polygonPoints = malloc(numberOfCoordinates * sizeof(CLLocationCoordinate2D));
NSInteger index = 0;
for (NSDictionary *pointDict in points) {
polygonPoints[index] = CLLocationCoordinate2DMake([[pointDict valueForKeyPath:#"latitude"] floatValue], [[pointDict valueForKeyPath:#"longitude"] floatValue]);
index++;
}
MKPolygon *overlayPolygon = [MKPolygon polygonWithCoordinates:polygonPoints count:numberOfCoordinates];
overlayPolygon.title = [state valueForKey:#"name"];
[overlays addObject:overlayPolygon];
free(polygonPoints);
}
[self.stateMapView addOverlays:overlays];
I used the following code to provide stroke and fill colors:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay NS_AVAILABLE(10_9, 7_0);
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonRenderer *pv = [[MKPolygonRenderer alloc] initWithPolygon:overlay];
pv.fillColor = [UIColor redColor];
pv.strokeColor = [UIColor blackColor];
return pv;
}
return nil;
}
Do I need to do something to render the Title? I think I should enable a configuration or something but I'm new to MapView. Or I need to create a UILabel?
Overlays don't automatically show their titles like annotations can (in their callout actually) so there's nothing you "need to do" or any configuration that you can enable.
A simple workaround to show titles on overlays is, as you suggest, to create a UILabel.
However, this UILabel should be added to an annotation view that is positioned at each overlay's center.
A minor drawback (or maybe not) to this method is that the titles will not scale with the zoom of the map -- they'll stay the same size and can eventually collide and overlay with other titles (but you may be ok with this).
To implement this approach:
For each overlay, add an annotation (using addAnnotation: or addAnnotations:) and set the coordinate to the approximate center of the overlay and the title to the overlay's title.
Note that since MKPolygon implements both the MKOverlay and the MKAnnotation protocols, you don't necessarily need to create a separate annotation class or separate objects for each overlay. MKPolygon automatically sets its coordinate property to the approximate center of the polygon so you don't need to calculate anything. You can just add the overlay objects themselves as the annotations. That's how the example below does it.
Implement the mapView:viewForAnnotation: delegate method and create an MKAnnotationView with a UILabel in it that displays the title.
Example:
[self.stateMapView addOverlays:overlays];
//After adding the overlays as "overlays",
//also add them as "annotations"...
[self.stateMapView addAnnotations:overlays];
//Implement the viewForAnnotation delegate method...
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
{
//show default blue dot for user location...
return nil;
}
static NSString *reuseId = #"ann";
MKAnnotationView *av = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (av == nil)
{
av = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
av.canShowCallout = NO;
//add a UILabel in the view itself to show the title...
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 30)];
titleLabel.tag = 42;
titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.textColor = [UIColor blackColor];
titleLabel.font = [UIFont boldSystemFontOfSize:16];
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.minimumScaleFactor = 0.5;
[av addSubview:titleLabel];
av.frame = titleLabel.frame;
}
else
{
av.annotation = annotation;
}
//find the UILabel and set the title HERE
//so that it gets set whether we're re-using a view or not...
UILabel *titleLabel = (UILabel *)[av viewWithTag:42];
titleLabel.text = annotation.title;
return av;
}
The alternative approach is to create a custom overlay renderer and do all the drawing yourself (the polygon line, the stroke color, the fill color, and the text). See Draw text in circle overlay and Is there a way to add text using Paths Drawing for some ideas on how to implement that.

UILabel inside UIView produces rounded corner with "square corner"

I have an UIView which I created and set background color to white. This view contains UILabel, which is a class called BubbleView. (Sorry I cannot add a picture because you need reputation 10+ :(
PROBLEM:
1. The following code produces a gray Label with rounded corner with gray-border square corner tips. This is because the UIView produces the square corner tips. The UILabel is rounded. Please note that I already set the background of UIView to white.
2. My text string of the UILabel is hiding behind UIView, so it is not displayed.
I'd love to show you pictures, but I am new and I cannot add pictures until I get to 10+ reputations.
http://i.stack.imgur.com/CdRjy.png
http://i.stack.imgur.com/zCdCV.png
Here is my code for setting the text and the view:
BubbleView:
- (void)drawRect:(CGRect)rect
{
const CGFloat boxWidth = self.bubbleWidth;
const CGFloat boxHeight = self.bubbleHeight;
NSLog(#"text, width, height: %#, %f, %f", self.text, self.bubbleWidth, self.bubbleHeight);
CGRect boxRect = CGRectMake(
roundf(self.bounds.size.width - boxWidth) / 2.0f,
roundf(self.bounds.size.height - boxHeight) / 2.0f,
boxWidth,
boxHeight);
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:boxRect cornerRadius:14.0f];
[[UIColor colorWithWhite:0.3f alpha:0.8f] setFill];
[roundedRect fill];
NSDictionary *attributes = #{
NSFontAttributeName : [UIFont systemFontOfSize:16.0f],
NSForegroundColorAttributeName : [UIColor whiteColor]
};
CGPoint textPoint = CGPointMake(
self.center.x+boxWidth/2,
self.center.y+boxHeight/2);
NSLog(#"text point origin: %f, %f", textPoint.x, textPoint.y);
[self.text drawAtPoint:textPoint withAttributes:attributes];
}
Main View Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
[self setText];
}
-(void) viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self setText];
}
- (void) setText
{
NSString *textR = #"I need this text to show up on autolayout so that i could continue working";
UIFont* font = [UIFont fontWithName:#"HelveticaNeue" size:14.0f];
CGSize constraint = CGSizeMake(250,9999);
CGRect textRect = [textR boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:font}
context:nil];
BubbleView *hostView = [[BubbleView alloc] initWithFrame:CGRectMake(20.0f, 160.0f, textRect.size.width+20, textRect.size.height+20)];
hostView.bubbleWidth = textRect.size.width+20;
hostView.bubbleHeight = textRect.size.height+20;
hostView.text = textR;
hostView.layer.cornerRadius = 10.0f;
self.view.layer.masksToBounds = TRUE;
[hostView drawRect:textRect];
hostView.backgroundColor = [UIColor whiteColor];
self.detailsView = hostView;
//self.detailsView.backgroundColor = [UIColor whiteColor];
NSLog(#"size: %f, %f", textRect.size.width, textRect.size.height);
NSLog(#"origin: %f, %f - size: %f, %f, backgroundColor: #%#", self.detailsView.frame.origin.x, self.detailsView.frame.origin.y, self.detailsView.frame.size.width, self.detailsView.frame.size.height, self.detailsView.backgroundColor);
[self.view addSubview:self.detailsView];
self.hostSays.text = textR;
self.hostSays.textColor = [UIColor blueColor];
[self.view layoutSubviews];
}
SOLUTION (ONLY 1 PART):
OK so I managed to solve half of my problems. I had to add the following code in my BubbleView class (inside - (id)initWithFrame:(CGRect)frame). This got rid of the square angles! (I think Wain was the one who suggested this but I might've misunderstood him)...
[self setOpaque:NO];
[self setBackgroundColor:[UIColor clearColor]];
So...still have the other part 2 of problem to solve and I'm hoping someone has run into this issue before!
Set the background colour of the view and add the label as a subview. Set the frame to get your required padding. Do not implement drawRect.
Now, the view will draw the background colour and the label automatically and the label will draw the text (with its background colour and border settings).
I know when I create custom buttons I need to setMasksToBounds
+ (void) addBorderToButtons:(UIButton *) btn
{
// Round button corners
CALayer *btnLayer = [btn layer];
[btnLayer setMasksToBounds:YES];
[btnLayer setCornerRadius:15.0f];
// Apply a 1 pixel, black border around Buy Button
[btnLayer setBorderWidth:1.5f];
[btnLayer setBorderColor:[[UIColor blackColor] CGColor]];
}
Setting this changes
To this
If you want to save your coding approach you strongly need to add [super drawRect:rect] in your drawRect: method
- (void)drawRect:(CGRect)rect{
[super drawRect:rect];
YOUR CODE
}
In this case you will see your text in UILabel.
Also you should not call drawRect: directly. It will be called automatically in runtime:
BubbleView *hostView =
[[BubbleView alloc] initWithFrame:CGRectMake(20.0f,
160.0f,
textRect.size.width+20,
textRect.size.height+20)];
hostView.bubbleWidth = textRect.size.width+20;
hostView.bubbleHeight = textRect.size.height+20;
hostView.text = textR;
// hostView.layer.cornerRadius = 10.0f;
// self.view.layer.masksToBounds = TRUE;
// [hostView drawRect:textRect];
// hostView.backgroundColor = [UIColor whiteColor];
self.detailsView = hostView;