Using UILabel as a mask for a UIView [duplicate] - ios

I'm trying to add a UIView to a UILabel, so that the text is the view's mask, enabling me to do things like animated text backgrounds (much like the slide to unlock label on the lockscreen).
The way I was planning on doing it was using the mask property on the view's layer to mask it to the shape of the text. However, I cannot find a way to get the UILabel's text shape as a CALayer.
Is this even possible? I can only find solutions that override the -(void)drawRect: method in the UILabel, but this wouldn't give me much flexibility.

UIView added the maskView property in iOS 8.0. Now, just create a UILabel to use as a mask for a UIView:
Objective-C:
UILabel *label = [[UILabel alloc] initWithFrame:self.view.frame];
label.text = #"Label Text";
label.font = [UIFont systemFontOfSize:70];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
UIView* overlayView = [[UIView alloc] initWithFrame:self.view.frame];
overlayView.backgroundColor = [UIColor blueColor];
overlayView.maskView = label;
[self.view addSubview:overlayView];
Swift 2:
let label = UILabel.init(frame: view.frame)
label.text = "Label Text"
label.font = UIFont.systemFontOfSize(70)
label.textAlignment = .Center
label.textColor = UIColor.whiteColor()
let overlayView = UIView.init(frame: view.frame)
overlayView.backgroundColor = UIColor.blueColor()
overlayView.maskView = label
view.addSubview(overlayView)
This creates a crisp UILabel with UIColor.blueColor() color taken from overlayView.

mopsled's solution is more flexible if you building for iOS 8+. However, if you're looking for a pre-iOS 8 answer, here it is.
Thanks to Linuxios for pointing me to this question. The key is to use CATextLayer instead of UILabel.
Objective-C:
CGRect textRect = {0, 100, self.view.frame.size.width, 100}; // rect to display the view in
CATextLayer *textMask = [CATextLayer layer];
textMask.contentsScale = [UIScreen mainScreen].scale; // sets the layer's scale to the main screen scale
textMask.frame = (CGRect){CGPointZero, textRect.size};
textMask.foregroundColor = [UIColor whiteColor].CGColor; // an opaque color so that the mask covers the text
textMask.string = #"Text Mask"; // your text here
textMask.font = (__bridge CFTypeRef _Nullable)([UIFont systemFontOfSize:30]); // your font here
textMask.alignmentMode = kCAAlignmentCenter; // centered text
UIView* view = [[UIView alloc] initWithFrame:textRect];
view.backgroundColor = [UIColor blueColor];
view.layer.mask = textMask; // mask the view to the textMask
[self.view addSubview:view];
Swift:
let textRect = CGRect(x: 0, y: 100, width: view.frame.size.width, height: 100) // rect to display the view in
let textMask = CATextLayer()
textMask.contentsScale = UIScreen.mainScreen().scale // sets the layer's scale to the main screen scale
textMask.frame = CGRect(origin: CGPointZero, size: textRect.size)
textMask.foregroundColor = UIColor.whiteColor().CGColor // an opaque color so that the mask covers the text
textMask.string = "Text Mask" // your text here
textMask.font = UIFont.systemFontOfSize(30) // your font here
textMask.alignmentMode = kCAAlignmentCenter // centered text
let bgView = UIView(frame: textRect)
bgView.backgroundColor = UIColor.blueColor()
bgView.layer.mask = textMask // mask the view to the textMask
view.addSubview(bgView)

I create the custom maskLabel.
Check the answer
https://stackoverflow.com/a/47105664/7371852
This is the result.

Related

UILabel as tableViewCell's accessoryView is shifted up

I am creating a normal UILabel and setting it as tableViewCell's accessoryView. My understanding is, accessoryView stays vertically centre aligned inside cell. But that is not happening. As I decrease the label text's font, the accessory view moves more upwards. Button works just fine. Problem with UILabel.
Here is my code :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = "Hello"
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 42, height: 21))
label.backgroundColor = UIColor.red
label.font = UIFont(name: "Helvetica Neue", size: 12)
label.highlightedTextColor = UIColor.white
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.green
label.textAlignment = .right;
label.clipsToBounds = true
label.autoresizesSubviews = true
label.contentMode = .left
label.text = "123";
cell.accessoryView = label
return cell
}
As #Faizyy has noticed, there's an issue with iOS 13, however providing small height is not always an option. My approach was to put UILabel into UIView of the same size as UILabel and use that container view as accessory view. This has solved alignment issues for me. Here's an example:
cell.accessoryView = [self getBadge:unread];
-(UIView *)getBadge:(int)count {
CGFloat size = 28;
CGFloat digits = [[#(count) stringValue] length];
CGFloat width = MAX(size, 0.7 * size * digits);
UILabel *badge = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width, size)];
badge.text = [#(count) stringValue];
badge.layer.cornerRadius = size / 2;
badge.layer.masksToBounds = YES;
badge.textAlignment = NSTextAlignmentCenter;
badge.font = [UIFont fontWithName:#"AvenirNext-Medium" size:16];
badge.textColor = UIColor.whiteColor;
badge.backgroundColor = VSColor.blue;
UIView *badgeHolder = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, size)];
badgeHolder.backgroundColor = UIColor.clearColor;
[badgeHolder addSubview:badge];
return badgeHolder;
}
If you are working pre SwiftUI, you should consider using auto layout to center the UILabel to the cell to prevent it from moving up. If you're using SwiftUI, you have to use SwiftUI Layout System. Hope that helps.
I found a solution. The frame that I was giving to UILabel was CGRect(x: 0, y: 0, width: 42, height: 21). If I give the height same as the text size i.e. 12.0, The label becomes centre aligned :).
This seems like an iOS 13 issue. Not seen in iOS 12 devices.
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 42, height: 12))

Adding space in UITextField After image

My problem is that I have created a textfield which has leftviewmode as an image and the placeholder is in somewhere middle of textfield where the text editing should get start when user starts typing. So how to get my text started from middle of textfield just where the placeholder is. I cannot set the leftviewmode in imageview because I already added image there. What should I do?
UIImageView *image = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"a_point30"]];
image.frame = CGRectMake(0, 0, image.image.size.width, image.image.size.height-10);
self.enterSource.leftViewMode = UITextFieldViewModeAlways; self.enterSource.leftView = image;
For example textediting should began from hello world placeholder.
You can do something like this,
UIImageView *imgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:imgName]];
imgView.frame = CGRectMake(0.0, 0.0, dropDown.image.size.width+10.0, dropDown.image.size.height);
imgView.contentMode = UIViewContentModeLeft;
textField.leftView = imgView;
textField.leftViewMode = UITextFieldViewModeAlways;
In Swift 2.2.1, it will be something like this:
let imageView = UIImageView(image: UIImage(named: "ImageName"))
imageView.frame = CGRectMake(0.0, 0.0, imageView.image!.size.width + 10, imageView.image!.size.height)
imageView.contentMode = UIViewContentMode.Left
textField.leftView = imageView
textField.leftViewMode = UITextFieldViewMode.Always

Adjust navigation bar title font size to fit text

Is it possible to adjust the font size of the navigation bar's title to fit the text?
You can create a UILabel and set it to UINavigationItem's titleView. See Apple doc: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationItem_Class/#//apple_ref/occ/instp/UINavigationItem/titleView
For the created UILabel, you can set the adjustsFontSizeToFitWidth & minimumScaleFactor properties to let it fit the title. Doc:
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UILabel_Class/#//apple_ref/occ/instp/UILabel/adjustsFontSizeToFitWidth
Some codes:
- (void)setMyTitle:(NSString *)title
{
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.navigationController.view.bounds.size.width - 100, 44)];
titleLabel.text = title;
titleLabel.font = [UIFont systemFontOfSize:16];
titleLabel.textColor = ...
...
self.navigationItem.titleView = titleLabel;
}
Try the following in viewDidLoad:
NSDictionary *attributes = #{ NSFontAttributeName: [UIFont fontWithName:#"HelveticaNeue-Bold" size:14] };
[self.navigationController.navigationBar setTitleTextAttributes:attributes];
NOTE: The downside to this solution is that you have to know the font size up front and set it manually. I'm not sure if you can set the navigation bar's title label to automatically change the font size to fit the text.
EDIT:
Turns out you can set the navigation bar's label to resize the font size dynamically
Swift 5
private var isFirst = true
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard isFirst else {
return
}
isFirst = false
// Adjust Navigation Title by text
if let navigationController = navigationController {
let bar = navigationController.navigationBar
var minX: CGFloat = 0
var maxX: CGFloat = bar.bounds.width
if let lastLeft = navigationItem.leftBarButtonItems?.last, let leftView = lastLeft.view, let buttonBarStackViewFrame = leftView.superview?.frame {
minX = buttonBarStackViewFrame.maxX
}
if let firstRight = navigationItem.rightBarButtonItems?.first, let rightView = firstRight.view, let buttonBarStackViewFrame = rightView.superview?.frame {
maxX = buttonBarStackViewFrame.minX
}
let titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: maxX - minX, height: bar.bounds.height))
titleLabel.text = LocStr(.favorite_master_title)
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.minimumScaleFactor = 0.3
titleLabel.textColor = UIColor.white
titleLabel.textAlignment = .center
if UIDevice.current.modelName.contains(PLUS) {
titleLabel.font = UIFont(name: GENERAL_FONT_NAME, size: PLUS_NAVIGATION_BAR_TITLE_FONT)!
} else {
titleLabel.font = UIFont(name: GENERAL_FONT_NAME, size: GENERAL_NAVIGATION_BAR_TITLE_FONT)!
}
navigationItem.titleView = titleLabel
}
}
with a Extension
extension UIBarButtonItem {
var view: UIView? {
return value(forKey: "view") as? UIView
}
}

UILabel resizes its SuperView?

I have the following view which contains a UILabel:
class MyView : UIView {
func viewDidLoad() {
self.autoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth
bottomView = UIView(frame: CGRectMake(self.bounds.origin.x, self.bounds.origin.y + self.imageView!.bounds.size.width, self.bounds.size.width, self.bounds.size.height - self.imageView!.bounds.size.height))
// bottomView frame calculation is: (0.0, 355.0, 355.0, 130.0)
bottomView?.backgroundColor = UIColor.greenColor()
bottomView?.autoresizingMask = UIViewAutoresizing.FlexibleWidth
bottomView?.clipsToBounds = true
self.addSubview(self.bottomView!)
var descriptionRect: CGRect = CGRectInset(self.bottomView!.bounds, leftRightInset, 20/2)
let descriptionLabel = UILabel()
descriptionLabel.numberOfLines = 3
descriptionLabel.autoresizingMask = UIViewAutoresizing.FlexibleWidth
descriptionLabel.font = UIFont(name: MGFont.helvetica, size: 22)
descriptionLabel.textColor = UIColor.whiteColor()
descriptionLabel.textAlignment = NSTextAlignment.Left
descriptionLabel.backgroundColor = UIColor.blueColor()
var paragraphStyle:NSMutableParagraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 1.0
paragraphStyle.lineBreakMode = NSLineBreakMode.ByTruncatingTail
let attributes = [NSParagraphStyleAttributeName : paragraphStyle]
descriptionLabel.attributedText = NSAttributedString(string: previewCard.title, attributes:attributes)
bottomView?.addSubview(descriptionLabel)
descriptionLabel.bounds = descriptionRect
descriptionLabel.sizeToFit()
descriptionLabel.center = CGPointMake(bottomView!.bounds.width/2, bottomView!.bounds.height/2 - hotelNameLableHeight/2)
}
}
The height of the bottomView should always be fixed.
MyView is resized during runtime. This means that the green bottom view also increases in size.
Here is the result when the label has two and three lines:
It appears that the UILabel resizes its super view.
Note that I do not use AutoLayout.
override func layoutSubviews() {
super.layoutSubviews()
}
How can I prevent the UILabel from resizing its SuperView?
Edit: I also tried to comment bottomView?.clipsToBounds = true
Override setFrame: and setBounds: of the super view (subclass if they're plain UIViews), add breakpoints, and see the stack trace to find out what's causing them to resize.
There is no need to set the autoResizingMask on the label, just set the frame and it will get automatically centered. And of course you can set the insets for the UILabel accordingly. I've add below testing code FYI:
override func viewDidLayoutSubviews() {
addTestView(CGRectMake(0, 200, view.bounds.width, 50), labelStr: "I am a short testing label")
addTestView(CGRectMake(0, 260, view.bounds.width, 50), labelStr: "I am a very longlonglonglonglonglonglong testing label")
addTestView(CGRectMake(0, 320, view.bounds.width, 50), labelStr: "I am a very longlonglonglonglonglonglonglonglonglonglonglong testing label. Will be truncated")
}
func addTestView(frame:CGRect, labelStr: String){
let bottomView = UIView(frame:frame)
bottomView.backgroundColor = UIColor.greenColor()
bottomView.autoresizingMask = UIViewAutoresizing.FlexibleWidth
bottomView.clipsToBounds = true
view.addSubview(bottomView)
var label = UILabel(frame: bottomView.bounds)
label.textAlignment = NSTextAlignment.Left
label.numberOfLines = 0
label.text = labelStr
bottomView.addSubview(label)
}

Display UIView with a UILabel text mask

I'm trying to add a UIView to a UILabel, so that the text is the view's mask, enabling me to do things like animated text backgrounds (much like the slide to unlock label on the lockscreen).
The way I was planning on doing it was using the mask property on the view's layer to mask it to the shape of the text. However, I cannot find a way to get the UILabel's text shape as a CALayer.
Is this even possible? I can only find solutions that override the -(void)drawRect: method in the UILabel, but this wouldn't give me much flexibility.
UIView added the maskView property in iOS 8.0. Now, just create a UILabel to use as a mask for a UIView:
Objective-C:
UILabel *label = [[UILabel alloc] initWithFrame:self.view.frame];
label.text = #"Label Text";
label.font = [UIFont systemFontOfSize:70];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
UIView* overlayView = [[UIView alloc] initWithFrame:self.view.frame];
overlayView.backgroundColor = [UIColor blueColor];
overlayView.maskView = label;
[self.view addSubview:overlayView];
Swift 2:
let label = UILabel.init(frame: view.frame)
label.text = "Label Text"
label.font = UIFont.systemFontOfSize(70)
label.textAlignment = .Center
label.textColor = UIColor.whiteColor()
let overlayView = UIView.init(frame: view.frame)
overlayView.backgroundColor = UIColor.blueColor()
overlayView.maskView = label
view.addSubview(overlayView)
This creates a crisp UILabel with UIColor.blueColor() color taken from overlayView.
mopsled's solution is more flexible if you building for iOS 8+. However, if you're looking for a pre-iOS 8 answer, here it is.
Thanks to Linuxios for pointing me to this question. The key is to use CATextLayer instead of UILabel.
Objective-C:
CGRect textRect = {0, 100, self.view.frame.size.width, 100}; // rect to display the view in
CATextLayer *textMask = [CATextLayer layer];
textMask.contentsScale = [UIScreen mainScreen].scale; // sets the layer's scale to the main screen scale
textMask.frame = (CGRect){CGPointZero, textRect.size};
textMask.foregroundColor = [UIColor whiteColor].CGColor; // an opaque color so that the mask covers the text
textMask.string = #"Text Mask"; // your text here
textMask.font = (__bridge CFTypeRef _Nullable)([UIFont systemFontOfSize:30]); // your font here
textMask.alignmentMode = kCAAlignmentCenter; // centered text
UIView* view = [[UIView alloc] initWithFrame:textRect];
view.backgroundColor = [UIColor blueColor];
view.layer.mask = textMask; // mask the view to the textMask
[self.view addSubview:view];
Swift:
let textRect = CGRect(x: 0, y: 100, width: view.frame.size.width, height: 100) // rect to display the view in
let textMask = CATextLayer()
textMask.contentsScale = UIScreen.mainScreen().scale // sets the layer's scale to the main screen scale
textMask.frame = CGRect(origin: CGPointZero, size: textRect.size)
textMask.foregroundColor = UIColor.whiteColor().CGColor // an opaque color so that the mask covers the text
textMask.string = "Text Mask" // your text here
textMask.font = UIFont.systemFontOfSize(30) // your font here
textMask.alignmentMode = kCAAlignmentCenter // centered text
let bgView = UIView(frame: textRect)
bgView.backgroundColor = UIColor.blueColor()
bgView.layer.mask = textMask // mask the view to the textMask
view.addSubview(bgView)
I create the custom maskLabel.
Check the answer
https://stackoverflow.com/a/47105664/7371852
This is the result.

Resources