Vertical alignment between CALayer and CATextLayer - ios

I'm currently working with CALayer and CATextLayer and I got a stuck when align them in vertical.
I'm create a CALayer for display an image inside it, then add a CATextLayer below CALayer after calculate the text size of CATexLayer, you can see my code for more details:
+ (instancetype)itemWithImage:(UIImage *)image andTitle:(NSString *)title {
CGSize size = CGSizeMake(MIN(image.size.width, [XFATLayoutAttributes itemWidth]), MIN(image.size.height, [XFATLayoutAttributes itemWidth]));
CALayer *layer = [CALayer layer];
layer.contents = (__bridge id)image.CGImage;
layer.bounds = CGRectMake(0, 0, MIN(size.width, [XFATLayoutAttributes itemImageWidth]), MIN(size.height, [XFATLayoutAttributes itemImageWidth]));
CATextLayer *label = [[CATextLayer alloc] init];
[label setFont:#"Helvetica"];
[label setFontSize:14];
label.alignmentMode = kCAAlignmentCenter;
label.backgroundColor = [UIColor redColor].CGColor;
label.string = title;
label.foregroundColor = [UIColor colorWithRed:138/255.0 green:138/255.0 blue:143/255.0 alpha:1.0].CGColor;
label.wrapped = YES;
label.contentsScale = [UIScreen mainScreen].scale;
CGSize stringSize = [title sizeWithAttributes:#{NSFontAttributeName: [UIFont fontWithName:#"Helvetica" size:14.0]}];
label.frame = CGRectMake(-5, layer.bounds.size.height + 5, stringSize.width, stringSize.height + 1);
[layer addSublayer:label];
return [[self alloc] initWithLayer:layer];
}
But the problem is the text size are not the same and my CATextLayer was misalignment a little bit like this image:
How can I align them in X axis? Please help.
Thanks in advance.

You can calculate right X offset this way:
label.frame = CGRectMake((size.width - stringSize.width) / 2, layer.bounds.size.height + 5,
stringSize.width, stringSize.height + 1);
Or you can simply make your label width equal to image width since you align text to center:
label.frame = CGRectMake(0, layer.bounds.size.height + 5,
size.width, stringSize.height + 1);
BTW I could not make work your method with return [[self alloc] initWithLayer:layer], so I had to return constructed subclass instance itself:
AlignLayer *layer = [AlignLayer layer]; // my subclass
// ...
return layer;
And Apple warns against using initWithLayer: in such situations:
This initializer is used to create shadow copies of layers, for example, for the presentationLayer method. Using this method in any other situation will produce undefined behavior. For example, do not use this method to initialize a new layer with an existing layer’s content.

Related

UILabelView cropping when expanded, but renders fully when inspected in view hierarchy

Image 1 shows issue. label has flexible width & height constrains, also we adding fix width & height to expand with. in View hierarchy it shows fully expanded as expected, but issue in real view:
Image 2 shows fully expanded label in View hierarchy:
Lable code
self.textLabel = [[RAVerticallyAlignedLabel alloc] initWithFrame: self.bounds andVerticalTextAlignment: [self verticalTextAlignment]];
self.textLabel.backgroundColor = ClearColour;
self.textLabel.userInteractionEnabled = NO;
self.textLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.textLabel.numberOfLines = 0;
viewToTruncate = self.textLabel;
textWidth = MAX(MIN(screenSize.width - 20, [label.text sizeWithAttributes: #{NSFontAttributeName : label.font}].width + 10);
[UIView animateWithDuration: 0.3 animations: ^
{
self->containerView.frame = CGRectMake(xPos, top , MIN(textWidth, screenSize.width - 2 * RAPadding), newTextviewHeight);
// Container view is added inside scroll view
self->containerView.layer.shadowRadius = kRATruncationUtiltityShadowRadius;
self->containerView.layer.shadowOpacity = kRATruncationUtiltityShadowOpacity;
self->containerView.layer.shadowOffset = kRATruncationUtiltityShadowOffset;
self->containerView.layer.shadowPath = [UIBezierPath bezierPathWithRect: self->containerView.bounds].CGPath;
self->containerView.layer.masksToBounds = NO;
self->viewToExpand.frame = CGRectMake(0, 0, textWidth + componentContentWidth, self->containerView.height);
//viewToExpand is UILable, added inside container view
self->viewToExpand.layer.borderWidth = 1.0;
self->viewToExpand.layer.borderColor = borderColor.CGColor;
self->viewToExpand.layer.backgroundColor = backgroundColor.CGColor;
}];
Found issue fix, it size wasn't issue but "gradientMask" was playing up on expand as below:
CAGradientLayer *gradientMask = [CAGradientLayer layer];
gradientMask.frame = CGRectMake(0, 0, viewToTruncate.width, viewToTruncate.height);
gradientMask.colors = #[(id)WhiteColour.CGColor,(id)WhiteColour.CGColor, (id)ClearColour.CGColor];
gradientMask.locations = #[#0, #(locationOfFade), #1];
viewToTruncate.layer.mask = gradientMask;
removing mask on expand , it was rendering as expected.

Is it possible to create a UITextField in iOS 6 with only a bottom border?

I want to customize a UITextField in iOS 6 which will look like a transparent text field with only a bottom border.I have found one image in stackoverflow earlier,but as I don't have 10 reputation I can't post a image,but I am posting the link below.
Text field with bottom border only in ios6
There are multiple ways to accomplish this:
Create a seperatorView and add it subview to UITextfield with y axis as the height of textfield.
UITextfield *namefield = [[UITextfield alloc] initwithframe:CGRectMake(20,40,70,40) ];
UIView *seperator=[UIView new];
seperator.backgroundColor = [UIColor grayColor];
seperator.frame =CGRectMake(namefield.frame.origin.x, namefield frame.origin.y+namefield.frame.size.height-1, namefield.frame.size.width,1);
[namefield addSubView:seperator];
By using the CoreAnimation Class
CALayer *bottomBorder = [CALayer layer];
CGFloat borderWidth = 1;
bottomBorder.borderColor = [UIColor grayColor].CGColor;
bottomBorder.frame = CGRectMake(0, textField.frame.size.height - borderWidth, textField.frame.size.width, textField.frame.size.height);
bottomBorder.borderWidth = borderWidth;
[textField.layer addSublayer:border];
textField.layer.masksToBounds = YES;
By creating a custom class for UITextField and overriding drawRect method.
Using a image and adding as a backgroundColor to UITextfield.

iOS: Why does UITextField with setCornerRadius cut off text

I'm trying to load a UITextField with rounded corners into a table view for iOS. For the most part, everything is working fine. However, the left-most character gets partially cut off due to the corner radius property. Is there a way to set a margin on the text that's inputted into a UITextField, so that it displays properly? Here's my code:
textInput = [[UITextField alloc] init];
textInput.backgroundColor = [UIColor whiteColor];
textInput.placeholder = #"example#gmail.com";
textInput.textColor = [UIColor blackColor];
textInput.keyboardType = UIKeyboardTypeEmailAddress;
textInput.returnKeyType = UIReturnKeyDone;
[[textInput layer] setBorderColor:[[UIColor whiteColor] CGColor]];
[[textInput layer] setBorderWidth:2.3];
[[textInput layer] setCornerRadius:15];
[textInput setClipsToBounds: YES];
[textInput setDelegate:self];
[self.contentView addSubview:textInput];
[textInput release];
I figured out a solution. I created a custom textfield, subclassed from UITextField:
#import "CR_customTextField.h"
#implementation CR_customTextField
- (CGRect)textRectForBounds:(CGRect)bounds {
int margin = 10;
CGRect inset = CGRectMake(bounds.origin.x + margin, bounds.origin.y, bounds.size.width - margin, bounds.size.height);
return inset;
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
int margin = 10;
CGRect inset = CGRectMake(bounds.origin.x + margin, bounds.origin.y, bounds.size.width - margin, bounds.size.height);
return inset;
}
#end
Use this
[textInput sizeToFit];
it May Helps you
textInput.layer.sublayerTransform = CATransform3DMakeTranslation(5, 0, 0);
//helps to start writing text from given pixel

How can I access the standard viewForHeaderInSection for a tableView?

I've got an indexed UITableView with individual sections. I would like to use a different background color for the header views in each section. I know I can completely roll my own view by implementing tableView:viewForHeaderInSection: (for example, see question # 2898361), but that seems to be "too much work" to me - the standard view looks fine, I would just have to change its background color.
But how do I access this standard view? I can't use [super tableView:viewForHeaderInSection:] because this is a question of implementing a protocol and not an issue of inheritance. Any other way I can get the standard view?
I'm almost certain you can't do this easily. I used one of my tech support request on my dev account recently asking about altering the background and borders of UITableView sections. The apple engineer told me that this really wasn't an easy thing to do, and even if you managed to do it, you would probably affect performance. He also pointed me to cocoawithlove and an article about editing uitableviews:
http://cocoawithlove.com/2009/08/adding-shadow-effects-to-uitableview.html
Really, creating your own header isn't that much effort. Below is some code I pulled out of one of my projects - it was commented out, so might not work straight away - but you can get the idea:
- (CAGradientLayer *) greyGradient {
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.startPoint = CGPointMake(0.5, 0.0);
gradient.endPoint = CGPointMake(0.5, 1.0);
UIColor *color1 = [UIColor colorWithRed:255.0f/255.0f green:255.0f/255.0f blue:255.0f/255.0f alpha:1.0];
UIColor *color2 = [UIColor colorWithRed:240.0f/255.0f green:240.0f/255.0f blue:240.0f/255.0f alpha:1.0];
[gradient setColors:[NSArray arrayWithObjects:(id)color1.CGColor, (id)color2.CGColor, nil]];
return gradient;
}
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
CGFloat width = CGRectGetWidth(tableView.bounds);
CGFloat height = [self tableView:tableView heightForHeaderInSection:section];
UIView *container = [[[UIView alloc] initWithFrame:CGRectMake(0,0,width,height)] autorelease];
container.layer.borderColor = [UIColor grayColor].CGColor;
container.layer.borderWidth = 1.0f;
CAGradientLayer *gradient = [self greyGradient];
gradient.frame = container.bounds;
[container.layer addSublayer:gradient];
UILabel *headerLabel = [[[UILabel alloc] initWithFrame:CGRectMake(12,0,width,height)] autorelease];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.font= [UIFont boldSystemFontOfSize:19.0f];
headerLabel.shadowOffset = CGSizeMake(1, 1);
headerLabel.textColor = [UIColor whiteColor];
headerLabel.shadowColor = [UIColor darkGrayColor];
NSString *title = [self tableView:tableView titleForHeaderInSection:section];
headerLabel.text = title;
return container;
}
Make sure to
#import <QuartzCore/QuartzCore.h>
By the way... this isn't supposed to mimic the look of the standard headers - its just an example. But I'm sure with a bit of trial and error you could alter this to mimic the standard ones and then change the colors slightly.
Although the other answers correctly point out you cannot access the default view to make simple modifications to it, if you have nothing to customize for a particular section header, you can return nil from tableView:viewForHeaderInSection: and the table view will use the default view.
This is helpful if you only need to customize some of your headers.
For whatever reason this is undocumented.
There is one problem with #bandejapalsa solution: the previous cell's separator is still visible with this implementation where as it is not on the default iOS sectionHeaderView. The solution I found was to use a CALayer and offset it by 1 pix. The image needs to be 1pix taller than the view frame itself.
// Create the view for the header
CGRect aFrame =CGRectMake(0, 0, tableView.contentSize.width, IMIUICustomisation.sectionHeaderViewHeight);
UIView * aView = [[UIView alloc] initWithFrame:aFrame];
aView.backgroundColor = UIColor.clearColor;
// Create a stretchable image for the background that emulates the default gradient, only in green
UIImage *viewBackgroundImage = [[UIImage imageNamed:#"greenheader.png"] stretchableImageWithLeftCapWidth:12 topCapHeight:0];
// Cannot set this image directly as the background of the cell because
// the background needs to be offset by 1pix at the top to cover the previous cell border (Alex Deplov's requirement ^_^)
CALayer *backgroungLayer = [CALayer layer];
backgroungLayer.frame = CGRectMake(0, -1, tableView.contentSize.width, IMIUICustomisation.sectionHeaderViewHeight+1);
backgroungLayer.contents = (id) viewBackgroundImage.CGImage;
backgroungLayer.masksToBounds = NO;
backgroungLayer.opacity = 0.9;
[aView.layer addSublayer:backgroungLayer];
// Take care of the section title now
UILabel *aTitle = [[UILabel alloc] initWithFrame: CGRectMake(10, 0, aView.bounds.size.width-10, aView.bounds.size.height)];
aTitle.text = [delegate tableView:tableView titleForHeaderInSection:section];
aTitle.backgroundColor = UIColor.clearColor;
aTitle.font = [UIFont boldSystemFontOfSize:18];
aTitle.textColor = UIColor.whiteColor;
// Text shadow
aTitle.layer.shadowOffset = CGSizeMake(0, 1);
aTitle.layer.shadowRadius = .2;
aTitle.layer.masksToBounds = NO;
aTitle.layer.shadowOpacity = 0.5;
aTitle.layer.shadowColor = IMIUICustomisation.selectedElementTextShadowColor.CGColor;
[aView addSubview:aTitle];
return aView;

How to calculate UILabel width based on text length?

I want to display an image next to a UILabel, however UILabel has variable text length, so I don't know where to place the image. How can I accomplish this?
CGSize expectedLabelSize = [yourString sizeWithFont:yourLabel.font
constrainedToSize:maximumLabelSize
lineBreakMode:yourLabel.lineBreakMode];
What is -[NSString sizeWithFont:forWidth:lineBreakMode:] good for?
this question might have your answer, it worked for me.
For 2014, I edited in this new version, based on the ultra-handy comment by Norbert below! This does everything.
// yourLabel is your UILabel.
float widthIs =
[self.yourLabel.text
boundingRectWithSize:self.yourLabel.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName:self.yourLabel.font }
context:nil]
.size.width;
NSLog(#"the width of yourLabel is %f", widthIs);
yourLabel.intrinsicContentSize.width for Objective-C / Swift
In swift
yourLabel.intrinsicContentSize().width
The selected answer is correct for iOS 6 and below.
In iOS 7, sizeWithFont:constrainedToSize:lineBreakMode: has been deprecated. It is now recommended you use boundingRectWithSize:options:attributes:context:.
CGRect expectedLabelSize = [yourString boundingRectWithSize:sizeOfRect
options:<NSStringDrawingOptions>
attributes:#{
NSFontAttributeName: yourString.font
AnyOtherAttributes: valuesForAttributes
}
context:(NSStringDrawingContext *)];
Note that the return value is a CGRect not a CGSize. Hopefully that'll be of some assistance to people using it in iOS 7.
Swift 4 Answer who are using Constraint
label.text = "Hello World"
var rect: CGRect = label.frame //get frame of label
rect.size = (label.text?.size(attributes: [NSFontAttributeName: UIFont(name: label.font.fontName , size: label.font.pointSize)!]))! //Calculate as per label font
labelWidth.constant = rect.width // set width to Constraint outlet
Swift 5 Answer who are using Constraint
label.text = "Hello World"
var rect: CGRect = label.frame //get frame of label
rect.size = (label.text?.size(withAttributes: [NSAttributedString.Key.font: UIFont(name: label.font.fontName , size: label.font.pointSize)!]))! //Calculate as per label font
labelWidth.constant = rect.width // set width to Constraint outlet
In iOS8 sizeWithFont has been deprecated, please refer to
CGSize yourLabelSize = [yourLabel.text sizeWithAttributes:#{NSFontAttributeName : [UIFont fontWithName:yourLabel.font size:yourLabel.fontSize]}];
You can add all the attributes you want in sizeWithAttributes.
Other attributes you can set:
- NSForegroundColorAttributeName
- NSParagraphStyleAttributeName
- NSBackgroundColorAttributeName
- NSShadowAttributeName
and so on. But probably you won't need the others
CGRect rect = label.frame;
rect.size = [label.text sizeWithAttributes:#{NSFontAttributeName : [UIFont fontWithName:label.font.fontName size:label.font.pointSize]}];
label.frame = rect;
Here's something I came up with after applying a few principles other SO posts, including Aaron's link:
AnnotationPin *myAnnotation = (AnnotationPin *)annotation;
self = [super initWithAnnotation:myAnnotation reuseIdentifier:reuseIdentifier];
self.backgroundColor = [UIColor greenColor];
self.frame = CGRectMake(0,0,30,30);
imageView = [[UIImageView alloc] initWithImage:myAnnotation.THEIMAGE];
imageView.frame = CGRectMake(3,3,20,20);
imageView.layer.masksToBounds = NO;
[self addSubview:imageView];
[imageView release];
CGSize titleSize = [myAnnotation.THETEXT sizeWithFont:[UIFont systemFontOfSize:12]];
CGRect newFrame = self.frame;
newFrame.size.height = titleSize.height + 12;
newFrame.size.width = titleSize.width + 32;
self.frame = newFrame;
self.layer.borderColor = [UIColor colorWithRed:0 green:.3 blue:0 alpha:1.0f].CGColor;
self.layer.borderWidth = 3.0;
UILabel *infoLabel = [[UILabel alloc] initWithFrame:CGRectMake(26,5,newFrame.size.width-32,newFrame.size.height-12)];
infoLabel.text = myAnnotation.title;
infoLabel.backgroundColor = [UIColor clearColor];
infoLabel.textColor = [UIColor blackColor];
infoLabel.textAlignment = UITextAlignmentCenter;
infoLabel.font = [UIFont systemFontOfSize:12];
[self addSubview:infoLabel];
[infoLabel release];
In this example, I'm adding a custom pin to a MKAnnotation class that resizes a UILabel according to the text size. It also adds an image on the left side of the view, so you see some of the code managing the proper spacing to handle the image and padding.
The key is to use CGSize titleSize = [myAnnotation.THETEXT sizeWithFont:[UIFont systemFontOfSize:12]]; and then redefine the view's dimensions. You can apply this logic to any view.
Although Aaron's answer works for some, it didn't work for me. This is a far more detailed explanation that you should try immediately before going anywhere else if you want a more dynamic view with an image and resizable UILabel. I already did all the work for you!!

Resources