How do I set a layout margin for a text field in swift? - ios

I am attempting to create margins in my text field so that when I go to type, the text isn't pressed so tightly against the edge.
I tried using this code (above viewDidLoad)
var insets = UIEdgeInsetsMake(10, 10, 10, 10)
Then putting this in my viewDidLoad()
textField.layoutMargins = insets
I ran the program and it still looked like there were no margins. How do I implement margins in a text field in Swift?

Subclass UITextField and implement textRectForBounds:. The simplest strategy is to call super, get the resulting rect, inset it as desired, and return it.
Here's an example result; note that the start and end of the text have considerable white space at the margin (of course the exact amount is up to you):

By creating new UIView with the right(your) values, you can set the padding in UITextField
textField.leftView = UIView(frame: CGRect(x: 30, y: 30, width: 100, height: 100))
textField.leftViewMode = UITextFieldViewMode.Always

Related

Changing UILabel title in custom UIButton without positional change

I have a UIButton with the label on the left side and UIImageView on the right side. The button is used to open a UIPicker. When a value is picked in the picker the same value is shown in button title. When the title changes (or, more accurately, when to uilabel has a width that screws the UI up) the title and icon is moved and the UI does not look good.
When a title with too long text is used the word is clipped and when it's too short the alignment is messed up.
I've tried changing the label frame so it can be constant, whatever the text, and left aligning the text so the jumping stops. I added adjustsFontSizeToFitWidth = true which kind of works, but with a longer title the text will get too small. I've also tried recreating/rerendering the button when the title changes but all attempts fail.
lazy var sortButton = { () -> UIButton in
let btn = UIButton()
btn.addTarget(self, action: #selector(sortButtonPressed), for: .touchUpInside)
btn.setTitle(NSLocalizedString("Sortera", comment: ""), for: .normal)
btn.titleLabel?.text = btn.titleLabel?.text?.uppercased()
btn.setImage(UIImage(named: "ios-down"), for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitleColor(Colors.FILTER_BUTTON_TEXT_COLOR, for: .normal)
btn.titleLabel?.adjustsFontSizeToFitWidth = true
btn.titleLabel?.font = UIFont(name: Fonts.AkzidenzGroteskProMd, size: 16)
btn.backgroundColor = Colors.BUTTON_BACKGROUND_GRAY
btn.imageView?.contentMode = .scaleAspectFit
btn.imageEdgeInsets = UIEdgeInsets(top: 16, left: (btn.titleLabel?.frame.size.width)! - buttonInsideOffset/2, bottom: 16, right: -(btn.titleLabel?.frame.size.width)! + buttonInsideOffset/2)
btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(btn.titleLabel?.frame.size.width)! + buttonInsideOffset, bottom: 0, right: (btn.titleLabel?.frame.size.width)! - buttonInsideOffset)
return btn
}()
I want all button to like like this, whatever the title text:
However, when the text is too small it looks like this:
or when it's too long:
You can do it in many way but the simplest way :
Take a UIView and then others two-element (a label a imageView) set in this View and make it look like button then set constraint as you want. Then addTarget to label and do all functionality to that that target selector method.
If you dont want button image to be shifted to the right or left then you have to constraint independent of the button's title label
btn.imageView?.frame = CGRect(x: 0, y: 0, width: 20, height: 20) // Or any size you want
// NB: I ommited left insets intentionally
btn.imageEdgeInsets.top = 16
btn.imageEdgeInsets.bottom = 16
btn.imageEdgeInsets.right = 16
Then constraint your label dependent to the imageView position, that way only label size will change without affecting the position of image.
btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(btn.titleLabel?.frame.size.width)! + buttonInsideOffset, bottom: 0, right: btn.imageView?.frame.width + 10)
Lastly, Since the frame is fixed size, I think you need to limit the font scaling factor to the min size you want and truncate the tail when that size is reached. If you don't want to truncate tail then you have to enable multiline titleLabel (Which I think you don't want this).
btn.titleLabel?.minimumScaleFactor = 0.5 // Or whatever minimum scale you wish
btn.titleLabel?.lineBreakMode = NSLineBreakMode.byTruncatingTail // Since button size is fixed and you want to limit font size then the best option is to truncate tail

iOS: AutoLayout a dynamic number of items

I'm trying to recreate the view shown below. The difficulty for me is that some of these elements will have a different number of instances.
First item is a UILabel with variabel multi lines. There will always be axactly one of this item.
UIImageView. This there can be zero or one item of.
UIButton zero or more (at least up to 3-4).
How to achieve this?
What would be a best practice to achieve this? I had one idea to use UITableView and just put each element in a table view cell. But it feels like a bit hacky solution. Especially since I then have to remove the default styling of the table (borders and padding).
Another solution is to maybe use a UICollectionView with only one column.
Third solution is to build on the autolayout solution I have, and achieve it with a lot of if statements and for-loops. This seems like a bad solution.
So how would I achieve this (using best practices)? And is it generally a bad idea for instance to use UITableView for pure layout purposes?
Create a UIStackView and inside it another one for every item with zero or more instances
Add when you want to append say a button to the buttons stackView use this
btnsStackView.addArrangedSubview(btn)
same for UILabels and UIImageViews
Check this it may help
for i in 0...5
{
let headerView:UIView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: self.view.bounds.size.height))
if(i % 2 == 0)
{
headerView.backgroundColor = UIColor.blue
}
else
{
headerView.backgroundColor = UIColor.yellow
}
self.stttq.addArrangedSubview(headerView)
}
let headerView:UIView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: self.view.bounds.size.height))
headerView.backgroundColor = UIColor.black
self.stttq.insertArrangedSubview(headerView , at:0)

Add labels to navigation bar

I am adding a calendar to my app and thought it would be cool to mimic the format of standard iOS Calendar app as per weekday labels:
In the Calendar app, these labels (S,M,T,W,T,F,S) seem to be integrated into the navigation bar, so I was wondering if there is a way to implement this or if this is something Apple left to themselves (as there seems to be no standard way to add anything but bar button items). Mind you, these labels should be dynamic - e.g. rearrange in case of day 2 as firstWeekDay for certain Locale.
Apple proposes not to resize navigationBar itself, but remove shadow from bar and add custom view under your navigationBar.
Please refer the apple recommended approach for extended navigation bars here - https://developer.apple.com/library/content/samplecode/NavBar/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007418-Intro-DontLinkElementID_2
And your particular scenario - https://developer.apple.com/library/content/samplecode/NavBar/Listings/NavBar_ExtendedNavBar_ExtendedNavBarViewController_swift.html#//apple_ref/doc/uid/DTS40007418-NavBar_ExtendedNavBar_ExtendedNavBarViewController_swift-DontLinkElementID_13
You just need to set a view to your title view of navigationItem for example:
sampleLabel.textAlignment = .center
sampleLabel.textColor = UIColor.white
sampleLabel.font = UIFont.systemFont(ofSize: 12, weight: .medium)
sampleLabel.frame = CGRect(x: 0, y: 0, width: 40, height: 30)
sampleLabel.text = "T"
let titleView = UIView
titleView.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
titleView.addSubview(sampleLabel)
navigationItem.titleView = titleView
You can add labels to your navigationBar like sampleLabel wherever you want.

iOS - UITextField: extend the bottom line for ANY resolution

I created a UITextFiled with a bottom line using this:
let Bottomline CALayer = ()
bottomLine.frame CGRect = (x: 0, y: usernameTextField.frame.height-7, width: usernameTextField.frame.width, height: 1)
bottomLine.backgroundColor = UIColor.black.cgColor
TextField.borderStyle = UITextBorderStyle.none
TextField.layer.addSublayer (Bottomline)
and the result of an iPhone 6 (right) is this:
Ok.
✄------------------------
The problem is to run the same application on a Pro iPad, because the
bottom line does not extend following the UITextField, but is shorter
This is the result on iPad Pro:
I do not understand why the bottom line does not follow the UITextField. When I called the bottom line I defined as:
bottomLine.frame CGRect = (x: 0, y: usernameTextField.frame.height-7, width: usernameTextField.frame.width, height: 1)
I have specified that the length of the line at the bottom must be:
width: usernameTextField.frame.width
What's the problem?
EDIT 1: The contrains are correct, because the UITextField adapts to
all types of resolution
EDIT:2 Thanks Matt! Now work!!!
I do not understand why the bottom line does not follow the UITextField
Because it's a layer. Layers do not automatically change size when their superlayer (the text field) changes size.
So, you need to redraw the bottom line every time the text field changes size.
At the moment, though, you are configuring the "bottom line" layer in viewDidLoad. So you are basing it on the frame that the text field has at that moment. But the text field has not yet attained its real size. Then it does change size, and meanwhile your "bottom line" layer just sits there — so now it is the wrong size.
An easy solution is to subclass UITextField and redraw the line every time layoutSubviews is called:
class MyTextField : UITextField {
var lineLayer : CALayer?
override func layoutSubviews() {
super.layoutSubviews()
self.lineLayer?.removeFromSuperlayer()
let bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0, y: self.bounds.height-7, width: self.bounds.width, height: 1)
bottomLine.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(bottomLine)
self.lineLayer = bottomLine
}
}
If your text field is a MyTextField, it will behave exactly as you desire.

How do you add an outline to text programmatically?

I have two apps, one with a UILabel and one using SpriteKit with a SKLabelNode. I'd like to add a black outline around the white text.
I can't find any outline or border properties or anything like that within swift. I've tried just creating new labels with slightly bigger, black font behind them but that didn't look right at all.
Here is my game with SpriteKit
title.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
title.text = "Tap to start!"
title.fontName = "Arial"
title.zPosition = 10
title.fontSize = 50
self.addChild(title)
And here is my other one that uses UILables (It's within a red rectangle so it's easier to see)
let rectangle = UIView(frame:CGRect(x: self.view.frame.size.width / 2 - 150, y: self.view.frame.size.height / 2 - 75, width: 300, height: 150))
rectangle.backgroundColor = UIColor.red
self.view.addSubview(rectangle)
let label = UILabel(frame: CGRect(x: rectangle.frame.size.width / 2 - 75, y: rectangle.frame.size.height / 2 - 10, width: 150, height: 20))
label.textAlignment = .center
label.text = "Tap to Start!"
label.textColor = UIColor.white
label.font = UIFont(name: "Arial", size: 20)
rectangle.addSubview(label)
How do I outline these labels?
For SKLabelNode's, there is no 'easy' way of outlining text. There is however a 'hack'. It involves adding 8 additional duplicate nodes around the label that are coloured black (or whatever colour you want the outline to be), have a zPosition less than the original and otherwise, be identical.
It's important that each label has the same font size as the original.
Your attempt at doing this involved increasing the font size of your one outline copy label. As you said in the question, it doesn't work. You have to use multiple labels so the effect appears seamlessly.
You would place one copy directly above the original, one directly below, one to the left, one to the right, then one on each corner (top right, top left, bottom right, and bottom left). This gives the effect of an outline, however is not efficient and should be taken with a grain of salt.
Note: Keep the amount shifted from the original consistent for all the copies, so the 'outline' is evenly sized.
This should work for UILabels too, however I think there might be an easier way to do this with UILabels.

Resources