Why row height is squeezing after scroll for tableView in swift? - ios

I am using a tableView and with the tableView I am using a label and my requirement is to make the label auto-adjusting according to the text. So I am calculating dynamic height for my label. It is working fine initially but after scroll the height of the label is reduced. I don't know why.
// to calculate dynamic height of the label
func height(constraintedWidth width: CGFloat, font: UIFont) -> CGFloat {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: .greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.text = self
label.font = font
label.sizeToFit()
return label.frame.height
}

you can try!
extension UILabel {
func textWidth() -> CGFloat {
return UILabel.textWidth(label: self)
}
func textHeight() -> CGFloat {
return UILabel.textHeight(withWidth: self.frame.width, font: self.font, text: self.text ?? "")
}
class func textWidth(label: UILabel) -> CGFloat {
return textWidth(label: label, text: label.text!)
}
class func textWidth(label: UILabel, text: String) -> CGFloat {
return textWidth(font: label.font, text: text)
}
class func textWidth(font: UIFont, text: String) -> CGFloat {
return textSize(font: font, text: text).width
}
class func textHeight(withWidth width: CGFloat, font: UIFont, text: String) -> CGFloat {
return textSize(font: font, text: text, width: width).height
}
class func textSize(font: UIFont, text: String, extra: CGSize) -> CGSize {
var size = textSize(font: font, text: text)
size.width = size.width + extra.width
size.height = size.height + extra.height
return size
}
class func textSize(font: UIFont, text: String, width: CGFloat = .greatestFiniteMagnitude, height: CGFloat = .greatestFiniteMagnitude) -> CGSize {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: height))
label.numberOfLines = 0
label.font = font
label.text = text
label.sizeToFit()
return label.frame.size
}
class func countLines(font: UIFont, text: String, width: CGFloat, height: CGFloat = .greatestFiniteMagnitude) -> Int {
// Call self.layoutIfNeeded() if your view uses auto layout
let myText = text as NSString
let rect = CGSize(width: width, height: height)
let labelSize = myText.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
return Int(ceil(CGFloat(labelSize.height) / font.lineHeight))
}
func countLines(width: CGFloat = .greatestFiniteMagnitude, height: CGFloat = .greatestFiniteMagnitude) -> Int {
// Call self.layoutIfNeeded() if your view uses auto layout
let myText = (self.text ?? "") as NSString
let rect = CGSize(width: width, height: height)
let labelSize = myText.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: self.font], context: nil)
return Int(ceil(CGFloat(labelSize.height) / self.font.lineHeight))
}
}

Related

Placeholder text not centered in iOS

I have created text field with Rounded Corners. All is good with the position of the cursor and also when i type the text. But the Placeholder text is off. Is there a way for the placeholder position to follow the Text field.
Below code overrides from the UITextField functions to reposition the cursor.
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: 0 + textOffset,
y: 0 + (textOffset / 2),
width: self.frame.width - textOffset,
height: self.frame.height + textOffset)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: 0 + textOffset,
y: 0 + (textOffset / 2),
width: self.frame.width - textOffset,
height: self.frame.height + textOffset)
Use this override method to set placeholderRect
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, paddingInsets)
}
You can use attributedPlaceholder paragraph styles you can center the placeholder in a UITextField.
let textField = UITextField(frame: CGRect(x: 0, y: 100, width: self.view.frame.size.width, height: 60))
textField.backgroundColor = UIColor.gray
let centeredParagraphStyle = NSMutableParagraphStyle()
centeredParagraphStyle.alignment = .center
let attributedPlaceholder = NSAttributedString(string: "Placeholder", attributes: [NSAttributedStringKey.paragraphStyle: centeredParagraphStyle])
textField.attributedPlaceholder = attributedPlaceholder
What worked for me was creating baselineOffset attribute and then setting the attributedPlaceholder.
let attributes = [NSAttributedString.Key.baselineOffset : NSNumber(2.0)]
myTextField.attributedPlaceholder = NSAttributedString(string: "Placeholder", attributes: attributes)

How UILabel height according to text height in Swift programmatically

I tried a many solutions but not get a right answer.
Here my code:
self.AhadeesView = UILabel()
self.AhadeesView.translatesAutoresizingMaskIntoConstraints = false
self.AhadeesView.backgroundColor = UIColor.orange
self.AhadeesView.numberOfLines = 0
self.AhadeesView.text = NSLocalizedString("TitleAhadees", comment: "Title Ahadees of the App")
self.AhadeesView.textAlignment = .center
self.AhadeesView.font = UIFont(name:"Jameel-Noori-Nastaleeq",size:25)
self.AhadeesView.lineBreakMode = NSLineBreakMode.byWordWrapping
// self.AhadeesView.sizeToFit()
containerView1.addSubview(AhadeesView)
Seems like you have missed your frame to show the label.
self.AhadeesView = UILabel(frame: [your frame value])
Frame can be created by
let frame = CGRect(x: 0, y: 0, width: 320, height: [height_of_String])
[height_of_String] can be calculated by below extension
You can calculate the width and height of the string using the following extension methods
extension String {
//To calculate the height u need to pass the width of the label and the required font size.
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(boundingBox.height)
}
func width(withConstraintedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(boundingBox.width)
}
}

Getting wrong text height?

I am using below code to determine the hight of text , it works fine for small text but if text is large it gives me wrong height(too much space at bottom of textview) how to fix that.
let textView = UITextView (frame: CGRectMake(0,0,maxWidth, 10))
textView.font = font
textView.text = text
//textView.textContainerInset = UIEdgeInsetsZero
let fixedWidth = textView.frame.size.width
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max))
var newFrame = textView.frame
newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
textView.frame = newFrame;
return textView.frame.height
This will be your text height in textview.
let textView = UITextView (frame: CGRectMake(0,0,maxWidth, 10))
textView.font = font
textView.text = text
let height = textView.conentSize.height
Use an extension on String
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return boundingBox.height
}
}
and also on NSAttributedString (which is very useful at times)
extension NSAttributedString {
func height(withConstrainedWidth width: CGFloat) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)
return boundingBox.height
}
func width(withConstrainedHeight height: CGFloat) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)
return boundingBox.width
}
}
Try with this method
static func neededHeigthForText(text:String,font:UIFont,maxWidth:CGFloat) ->CGFloat
{
let options : NSStringDrawingOptions = [.usesLineFragmentOrigin,.usesFontLeading]
let style : NSMutableParagraphStyle = NSMutableParagraphStyle()
style.lineBreakMode = .byWordWrapping
let textAttributes = [NSFontAttributeName:font]
let size = NSString(string: text).boundingRect(with: CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude) , options: options, attributes: textAttributes, context: nil).size
return size.height
}
I hope this helps you

overlaying text on image using swift

I am trying to overlay some text on an image using Swift and am looking at this code here: (src: How do I add text to an image in iOS Swift)
This places the text right in the center. I have been changing the values in
var rect = CGRectMake(10,150, inImage.size.width,
inImage.size.height)
but I am unable to get the text to show in the lower left corner. Can someone help and show what I am missing here ?
I am adding the modified image using this line:
modImage = self.textToImage("000", inImage: UIImage(named:"thisImage.png")!, atPoint: CGPointMake(10, 400))
Below is the function...
func textToImage(drawText: NSString, inImage: UIImage, atPoint: CGPoint) -> UIImage{
// Setup the font specific variables
var textColor = UIColor.whiteColor()
var textFont = UIFont(name: "Helvetica Bold", size: 12)!
// Setup the image context using the passed image
let scale = UIScreen.mainScreen().scale
UIGraphicsBeginImageContextWithOptions(inImage.size, false, scale)
// Setup the font attributes that will be later used to dictate how the text should be drawn
let textFontAttributes = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: textColor,
]
// Put the image into a rectangle as large as the original image
inImage.drawInRect(CGRectMake(0, 0, inImage.size.width, inImage.size.height))
// Create a point within the space that is as bit as the image
var rect = CGRectMake(atPoint.x, atPoint.y, inImage.size.width, inImage.size.height)
// Draw the text into an image
drawText.drawInRect(rect, withAttributes: textFontAttributes)
// Create a new image out of the images we have created
var newImage = UIGraphicsGetImageFromCurrentImageContext()
// End the context now that we have the image we need
UIGraphicsEndImageContext()
//Pass the image back up to the caller
return newImage
}
Details
xCode 9.1, Swift 4
Solution
extension UIView
extension UIView {
func copyObject<T: UIView> () -> T? {
let archivedData = NSKeyedArchiver.archivedData(withRootObject: self)
return NSKeyedUnarchiver.unarchiveObject(with: archivedData) as? T
}
}
extension UIImage
extension UIImage {
typealias EditSubviewClosure<T: UIView> = (_ parentSize: CGSize, _ viewToAdd: T)->()
func with<T: UIView>(view: T, editSubviewClosure: EditSubviewClosure<T>) -> UIImage {
if let copiedView = view.copyObject() as? T {
UIGraphicsBeginImageContext(size)
let basicSize = CGRect(origin: .zero, size: size)
draw(in: basicSize)
editSubviewClosure(size, copiedView)
copiedView.draw(basicSize)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
return self
}
}
extension UIImageView
extension UIImageView {
enum ImageAddingMode {
case changeOriginalImage
case addSubview
case addCopiedSubview
}
func drawOnCurrentImage<T: UIView>(view: T, mode: ImageAddingMode, editSubviewClosure: #escaping UIImage.EditSubviewClosure<T>) {
guard let image = image else {
return
}
let addSubView: (T) -> () = { view in
editSubviewClosure(self.frame.size, view)
self.addSubview(view)
}
switch mode {
case .changeOriginalImage:
self.image = image.with(view: view, editSubviewClosure: editSubviewClosure)
case .addSubview:
addSubView(view)
case .addCopiedSubview:
if let copiedView = view.copyObject() as? T {
addSubView(copiedView)
}
}
}
}
Usage
Sample 1
func sample1(label: UILabel) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "wall")?.with(view: label) { (parentSize, viewToAdd) in
print("parentSize: \(parentSize)")
viewToAdd.font = UIFont.systemFont(ofSize: 40)
viewToAdd.textColor = .yellow
viewToAdd.bounds = CGRect(x: 40, y: 40, width: 200, height: 40)
}
}
Sample 2
func sample2(label: UILabel) {
imageView.image = UIImage(named: "wall")
imageView.contentMode = .scaleAspectFit
imageView.drawOnCurrentImage(view: label, mode: .changeOriginalImage) { (parentSize, viewToAdd) in
print("parentSize: \(parentSize)")
viewToAdd.font = UIFont.systemFont(ofSize: 40)
viewToAdd.textColor = .yellow
viewToAdd.textAlignment = .right
let width: CGFloat = 200
let height: CGFloat = 30
let indent: CGFloat = 40
viewToAdd.bounds = CGRect(x: parentSize.width - width - indent, y: parentSize.height - height - indent, width: width, height: height)
}
}
Sample 3
func sample3(label: UILabel) {
imageView.image = UIImage(named: "wall")
imageView.contentMode = .scaleAspectFill
imageView.drawOnCurrentImage(view: label, mode: .addSubview) { (parentSize, viewToAdd) in
print("parentSize: \(parentSize)")
viewToAdd.font = UIFont.systemFont(ofSize: 16)
viewToAdd.textColor = .yellow
viewToAdd.frame = CGRect(x: 40, y: 40, width: 200, height: 20)
}
}
Sample 4
func sample4(label: UILabel) {
imageView.image = UIImage(named: "wall")
imageView.contentMode = .scaleAspectFill
imageView.drawOnCurrentImage(view: label, mode: .addCopiedSubview) { (parentSize, viewToAdd) in
print("parentSize: \(parentSize)")
viewToAdd.font = UIFont.systemFont(ofSize: 16)
viewToAdd.textColor = .yellow
viewToAdd.frame = CGRect(x: 40, y: 40, width: 200, height: 20)
}
}
Full sample
Do not forget to add the solution code here
import UIKit
class ViewController: UIViewController {
let imageView = UIImageView(frame: CGRect(x: 50, y: 50, width: 300, height: 300))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let label = UILabel(frame: CGRect(x: 20, y: 20, width: 80, height: 30))
label.text = "Blablabla"
label.font = UIFont.systemFont(ofSize: 20)
label.textColor = .black
view.addSubview(label)
sample1(label: label)
//sample2(label: label)
//sample3(label: label)
//sample4(label: label)
imageView.clipsToBounds = true
view.addSubview(imageView)
}
func sample1(label: UILabel) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "wall")?.with(view: label) { (parentSize, viewToAdd) in
print("parentSize: \(parentSize)")
viewToAdd.font = UIFont.systemFont(ofSize: 40)
viewToAdd.textColor = .yellow
viewToAdd.bounds = CGRect(x: 40, y: 40, width: 200, height: 20)
}
}
func sample2(label: UILabel) {
imageView.image = UIImage(named: "wall")
imageView.contentMode = .scaleAspectFit
imageView.drawOnCurrentImage(view: label, mode: .changeOriginalImage) { (parentSize, viewToAdd) in
print("parentSize: \(parentSize)")
viewToAdd.font = UIFont.systemFont(ofSize: 40)
viewToAdd.textColor = .yellow
viewToAdd.textAlignment = .right
let width: CGFloat = 200
let height: CGFloat = 30
let indent: CGFloat = 40
viewToAdd.bounds = CGRect(x: parentSize.width - width - indent, y: parentSize.height - height - indent, width: width, height: height)
}
}
func sample3(label: UILabel) {
imageView.image = UIImage(named: "wall")
imageView.contentMode = .scaleAspectFill
imageView.drawOnCurrentImage(view: label, mode: .addSubview) { (parentSize, viewToAdd) in
print("parentSize: \(parentSize)")
viewToAdd.font = UIFont.systemFont(ofSize: 16)
viewToAdd.textColor = .yellow
viewToAdd.frame = CGRect(x: 40, y: 40, width: 200, height: 20)
}
}
func sample4(label: UILabel) {
imageView.image = UIImage(named: "wall")
imageView.contentMode = .scaleAspectFill
imageView.drawOnCurrentImage(view: label, mode: .addCopiedSubview) { (parentSize, viewToAdd) in
print("parentSize: \(parentSize)")
viewToAdd.font = UIFont.systemFont(ofSize: 16)
viewToAdd.textColor = .yellow
viewToAdd.frame = CGRect(x: 40, y: 40, width: 200, height: 20)
}
}
}
Great answer Vasily!
but in my situation I needed to add these fixes.
TEXT IS VERY SMALL
I ran a situation where the text was super small, maybe because the UIImage size was too small for the selected font of 14, so I fixed this by giving a huge font of 120.
TEXT IS NOT DISPLAYED AT THE GIVEN CGPOINT
In the same context of a big UIImage size, the position given to the extension was misplacing the text, the fix: added these 2 lets with the ratio of height and width computed and then multiplied for x,y points.
let widthRatio = size.width/UIScreen.main.bounds.width
let heightRatio = size.height/UIScreen.main.bounds.height
Hope that helps anybody!

Add padding to Intrinsic Content Size not working in UILabel

I want to have extra paddings to these UILabels.I changed the intrinsic content size with the following code:
class CustomLabelWithPadding:UILabel{
override func intrinsicContentSize() -> CGSize {
let contentSize = super.intrinsicContentSize()
return CGSize(width: contentSize.width + 50, height: contentSize.height)
}
}
However the label is not having padding as
let label = CustomLabelWithPadding()
label.text = "Hello World"
label.font = UIFont(name: "Helvetica", size: 20)
label.sizeToFit()
label.layer.backgroundColor = UIColor(red: 0.067, green: 0.773, blue: 0.525, alpha: 1.00).CGColor
label.textAlignment = NSTextAlignment.Center
label.textColor = UIColor.whiteColor()
label.frame.origin.x = 160
label.frame.origin.y = 50
self.view.addSubview(label)
This one did the job :)
class CustomLabelWithPadding:UILabel{
var edgeInsets:UIEdgeInsets = UIEdgeInsetsZero
override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
var rect = super.textRectForBounds(UIEdgeInsetsInsetRect(bounds, edgeInsets), limitedToNumberOfLines: numberOfLines)
rect.size.width += 10;
return rect
}
override func drawTextInRect(rect: CGRect) {
self.clipsToBounds = true
self.layer.cornerRadius = 3
super.drawTextInRect(UIEdgeInsetsInsetRect(rect, edgeInsets))
}
}

Resources