Swift UILabel not setting correct y value - ios

I've confirmed, that the value given to "addLabel" function, is changing, but when printing out "label.frame.origin.y", it's always showing 0. At the moment, the code looks pretty bad, because I've been struggling with this issue for a while now. Any ideas?
var newY: Int = 8
var mainViewNewHeight: Int = 251
func setContentFromList(content: [String], target: UIView, targetHeight: NSLayoutConstraint) {
newY = 8
var bullet:UILabel!
let width = Int(UIScreen.main.bounds.width) - 84
let font = UIFont(name: "Helvetica", size: 18)
for item in content {
bullet = UILabel(frame: CGRect(x: 8, y: newY, width: 7, height: 17))
bullet.text = "•"
bullet.textColor = UIColor( red: 183/255, green: 110/255, blue:121/255, alpha: 1.0 )
bullet.font = bullet.font.withSize(18)
let a = CGFloat(newY)
print(a) // Correct
addLabel(y: a, text: item, font: font!, width: CGFloat(width), target: target)
target.addSubview(bullet)
}
mainViewNewHeight += newY
targetHeight.constant = CGFloat(newY)
}
And this is the "addLabel" function:
func addLabel(y: CGFloat, text:String, font:UIFont, width:CGFloat, target: UIView) {
let label:UILabel = UILabel(frame: CGRect(x: 22, y: y, width: width, height: CGFloat.greatestFiniteMagnitude))
print("Label y:", label.frame.origin.y) // Invalid (0.0)
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
newY += Int(label.frame.height)
target.addSubview(label)
}

Your issue is in the height: CGFloat.greatestFiniteMagnitude remove CGFloat.greatestFiniteMagnitude and replace it with an integer value and you´ll see that your code will work.
So basically:
let label:UILabel = UILabel(frame: CGRect(x: 22, y: y, width: width, height: 20))
Print out:
20.0
Label y: 20.0
41.0
Label y: 41.0
62.0
Label y: 62.0
83.0
Label y: 83.0

Related

Set section header padding of tableview for second header in old iOS

I'm setting header padding for my tableview in iOS older than 15 explicitly. This works for the top first header (section 1 of tableview), but doesn't work for second header (section 2 of tableview). How could I set it for the second also?
Is there something like tableView.tableHeaderView[2] or tableView.tableHeaderView.second ?
In code its the place with commented-out places.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if tableView.tag == 2 {
if section == 0 {
if self.tableView(tableView, numberOfRowsInSection: section) > 0 {
let view = UIView()
let imageView = UIImageView(image: UIImage(named: "Logo_face"))
if #unavailable(iOS 15.0) {
imageView.frame = CGRect(x: 10, y: 20, width: 25, height: 25)
} else {
imageView.frame = CGRect(x: 10, y: 0, width: 25, height: 25)
}
view.addSubview(imageView)
let label = UILabel()
label.text = "店舗"
label.frame = CGRect(x: 40, y: 0, width: 200, height: 25)
view.addSubview(label)
if #unavailable(iOS 15.0) { //setting here; works
let padding: CGFloat = 20
view.frame = CGRect(x: view.frame.origin.x - padding,
y: view.frame.origin.y - padding,
width: view.frame.width + 2 * padding,
height: view.frame.height + 2 * padding)
tableView.tableHeaderView = view // this is good
label.frame = CGRect(x: 40, y: 20, width: 200, height: 25)
}
return view
}
} else {
if self.tableView(tableView, numberOfRowsInSection: section) > 0 {
let view = UIView()
let imageView = UIImageView(image: UIImage(named: "pinIconForSearch"))
if #unavailable(iOS 15.0) {
imageView.frame = CGRect(x: 10, y: 20, width: 25, height: 25)
} else {
imageView.frame = CGRect(x: 10, y: 0, width: 25, height: 25)
}
view.addSubview(imageView)
let label = UILabel()
label.text = "検索候補"
label.frame = CGRect(x: 40, y: 0, width: 200, height: 25)
view.addSubview(label)
if #unavailable(iOS 15.0) { //setting here doesnt work
let padding: CGFloat = 20
view.frame = CGRect(x: view.frame.origin.x - padding,
y: view.frame.origin.y - padding,
width: view.frame.width + 2 * padding,
height: view.frame.height + 2 * padding)
tableView.tableHeaderView = view // this is not good
label.frame = CGRect(x: 40, y: 20, width: 200, height: 25)
}
return view
}
}
}
if #available(iOS 15.0, *) {
tableView.sectionHeaderTopPadding = 0.0
} else {}
return nil
}

macOS NSScrollView Not Scrolling Vertically

I am trying to add few views to see if the NSScrollView will scroll vertically but it is not doing anything.
private func configure2() {
var yOffset = 0
let scrollView = NSScrollView(frame: NSRect(x: 0, y: 0, width: 400, height: 900))
scrollView.hasVerticalScroller = true
for _ in 1...20 {
let v = NSView(frame: NSRect(x: 0, y: 0 + yOffset, width: 50, height: 20))
v.wantsLayer = true
v.layer?.backgroundColor = NSColor.red.cgColor
scrollView.addSubview(v)
yOffset += 40
}
scrollView.backgroundColor = NSColor.green
self.addSubview(scrollView)
}
I remember in UIKit I can set the contentSize property of UIScrollView but in macOS I cannot set contentSize.
You need to set either the documentView or contentView of your NSScrollView. Then, you'll add your subviews to that view.
private func configure2() {
var yOffset = 0
let scrollView = NSScrollView(frame: NSRect(x: 0, y: 0, width: 400, height: 900))
let documentView = NSView(frame: .zero)
scrollView.hasVerticalScroller = true
for _ in 1...20 {
let v = NSView(frame: NSRect(x: 0, y: 0 + yOffset, width: 50, height: 20))
v.wantsLayer = true
v.layer?.backgroundColor = NSColor.red.cgColor
documentView.addSubview(v)
yOffset += 40
}
print(yOffset)
documentView.frame = .init(x: 0, y: 0, width: 400, height: yOffset)
scrollView.documentView = documentView
scrollView.backgroundColor = NSColor.green
self.addSubview(scrollView)
}

Indent UITextField text and placeholder

I have programmatically created a UITextField in my UITableView footer like so:
let customView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 50))
self.textArea = UITextField(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width - 50, height: 50))
self.textArea.placeholder = "Add Item"
self.textArea.layer.borderColor = UIColor.gray.cgColor
self.textArea.layer.borderWidth = 1
customView.addSubview(self.textArea)
let button = UIButton(frame: CGRect(x: self.view.bounds.width - 50, y: 0, width: 50, height: 50))
button.setTitle("Add", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = UIColor(displayP3Red: 3.0 / 255.0, green: 0.0 / 255.0, blue: 113.0 / 255.0, alpha: 1.0)
button.addTarget(self, action: #selector(self.addWishListItem), for: .touchUpInside)
customView.addSubview(button)
self.tableView.tableFooterView = customView
My question is, how do I indent the UITextField text and placeholder text so it's not right at the edge of the left side?
I found this:
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: self.textArea.frame.height))
self.textArea.leftView = paddingView
self.textArea.leftViewMode = .always
Is there a better way on doing this?
You can create a customTextField and override the rects for text, placeholder, border.. basically everything.
import UIKit
class CustomTextField: UITextField {
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return CGRect.init(x: 10, y: -10, width: bounds.width, height: bounds.height)
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect.init(x: 10, y: -10, width: bounds.width, height: bounds.height)
}
}
You control the position of it by changing x and y values

Measuring the height of lines in UITextView

I am trying to measure the height of lines to place an overlay over specific text in a UITextView. Specifically what I mean by lines is text delimitated with \n. So one line can actually span multiple lines and that is what I am trying to measure. I have tried a few different approaches, however, I can't seem to calculate the height correctly. Another weakness with my current approaches is it has to be all calculated on the main synchronous thread which is less than ideal because I have a lot calculations to do.
EXAMPLE UI APPROACH
measurement.font = UIFont(name: "Menlo", size: 16)
measurement.frame = CGRect(x: 0, y: 0, width: 10000, height: 50)
func heightForString(line: String) -> CGFloat {
var height : CGFloat = 0
measurement.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 10000)
measurement.sizeToFit()
height = measurement.frame.height
print(height, lineHeight)
measurement.frame = CGRect(x: 0, y: 0, width: 10000, height: 50)
return height
}
// Comparing the returned height to measurement.font?.lineHeight and padding shows the results make little sense... :(
EXAMPLE PRINT
Should be 1 line
30.0 18.625
Should be 3 lines
43.6666666666667 18.625
func heightForString(line: String) -> CGFloat {
let textView = UITextView()
let maxwidth = UIScreen.main.bounds.width
textView.frame = CGRect(x:0,y: 0,width: maxwidth,height: CGFloat(MAXFLOAT))
textView.textContainerInset = UIEdgeInsets.zero
textView.textContainer.lineFragmentPadding = 0
textView.font = UIFont(name: "Menlo", size: 16)
textView.text = line
textView.isScrollEnabled = false
textView.sizeToFit()
return textView.frame.size.height
}

Quality of scaled-down UILabels

I'm using Swift 3 to create an iOS interface where some UIViews containing (amongst other things) UILabels are scaled up and down based on where they're being positioned on the screen. My first approach was to create and populate the views at a comfortably large size (say 100x100) and then scale them as needed using CGAffineTransform(scaleX:y:), however I've noticed that the downscaling of the text in the labels isn't graceful at all, and the text becomes pixelated and close to unreadable at small scales. As a comparison (see example below), changing the font size directly gives much better results, however the structure within my views is somewhat complex and having to redraw everything based on some size factor would be a hassle. Is there a better and smoother way to approach this problem?
Here's an example project I've created to illustrate the problem, as well as the output in the simulator (same as on the iPhone itself), downscaled views are on the left (red) and changed font sizes are the right (green).
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
for i in 1...10 {
let f = CGFloat(1.0) / CGFloat(i)
let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 150, height: 50))
view1.backgroundColor = UIColor.red
let label1 = UILabel(frame: CGRect(x: 0, y: 0, width: 150, height: 50))
label1.text = "\(100 / i)%"
label1.font = UIFont(name: "Verdana", size: 24.0)
label1.textAlignment = .right
view1.addSubview(label1)
view1.transform = CGAffineTransform(scaleX: f, y: f)
view1.center = CGPoint(x: 160 - 75.0 * f, y: CGFloat(60 * i) + 25.0 * f)
self.view.addSubview(view1)
let view2 = UIView(frame: CGRect(x: CGFloat(170), y: CGFloat(60 * i), width: 150 * f, height: 50 * f))
view2.backgroundColor = UIColor.green
let label2 = UILabel(frame: CGRect(x: 0, y: 0, width: 150 * f, height: 50 * f))
label2.text = "\(100 / i)%"
label2.font = UIFont(name: "Verdana", size: 24.0 * f)
view2.addSubview(label2)
self.view.addSubview(view2)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This might be an answer - but not really suitable for a comment, so...
Give this a try - it creates a 3rd "column" of yellow-background views, using .adjustsFontSizeToFitWidth. The font size will auto-adjust based on the size of the views that contain the labels.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
for i in 1...10 {
let f = CGFloat(1.0) / CGFloat(i)
let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 150, height: 50))
view1.backgroundColor = UIColor.red
let label1 = UILabel(frame: CGRect(x: 0, y: 0, width: 150, height: 50))
label1.text = "\(100 / i)%"
label1.font = UIFont(name: "Verdana", size: 24.0)
label1.textAlignment = .right
view1.addSubview(label1)
view1.transform = CGAffineTransform(scaleX: f, y: f)
view1.center = CGPoint(x: 160 - 75.0 * f, y: CGFloat(60 * i) + 25.0 * f)
self.view.addSubview(view1)
let view2 = UIView(frame: CGRect(x: CGFloat(170), y: CGFloat(60 * i), width: 150 * f, height: 50 * f))
view2.backgroundColor = UIColor.green
let label2 = UILabel(frame: CGRect(x: 0, y: 0, width: 150 * f, height: 50 * f))
label2.text = "\(100 / i)%"
label2.font = UIFont(name: "Verdana", size: 24.0 * f)
view2.addSubview(label2)
self.view.addSubview(view2)
let view3 = UIView(frame: CGRect(x: CGFloat(270), y: CGFloat(60 * i), width: 150 * f, height: 50 * f))
view3.backgroundColor = UIColor.yellow
let label3 = UILabel(frame: view3.bounds)
label3.text = "\(100 / i)%"
label3.font = UIFont(name: "Verdana", size: 24.0)
label3.adjustsFontSizeToFitWidth = true
label3.minimumScaleFactor = 0.05
label3.numberOfLines = 0
// we want the label to resize with the view, if the view frame changes
label3.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view3.autoresizesSubviews = true
view3.addSubview(label3)
self.view.addSubview(view3)
}
}

Resources