Add left and right padding to UIButton - swift

I would like to add some left and right padding to buttons, which I see they have a titleEdgeInsets property. Here's my code:
import UIKit
class QuickPromptButton: UIButton {
var userFacingValue: String?
var answerValue: String?
override init(frame: CGRect) {
super.init(frame: frame)
layer.borderColor = UIColor.primaryColor.cgColor
layer.borderWidth = 1
layer.cornerRadius = 15
setTitleColor(.primaryColor, for: .normal)
titleEdgeInsets = .init(top: 0, left: -10, bottom: 0, right: 10)
contentVerticalAlignment = .center
contentHorizontalAlignment = .center
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
However, this is what they look like now:
The problem is that the text goes out of the borders. Any idea why?
This is what I get by using 10 on both left and right:

override var intrinsicContentSize: CGSize {
let originalSize = super.intrinsicContentSize
let size = CGSize(width: originalSize.width + 20, height: originalSize.height)
return size
}
Overriding intrinsicContentSize and adding the total padding on top of the current width ended up working for me, although not sure if this is the right approach.

Related

How to indent text in a label? Swift, UIKit

I have a text in a label right aligned. The label is in a dynamic table cell. But the text is very close to the right edge of the label and does not look presentable. It looks something like this:
1234.5|
Update: How it looks on the screen. 0 is adjacent to the right side of the screen
I would like to move the text a little bit from the right edge to make it look like this:
1234.5--|
In the text field, I use a custom class for this and override the following functions:
private let padding = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 20)
override func textRect(forBounds bounds: CGRect) -> CGRect {
bounds.inset(by: padding)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
bounds.inset(by: padding)
}
The label has an UIEdgeInsets object too. But I could not find functions similar to the functions of techRect and EditingRect on the label.
It seems to me that the label should have such functions. Maybe they are called differently. But I have not yet been able to find them in the documentation and Google search has not helped either.
Maybe someone has already solved such a problem and could suggest either the solution itself or in which direction to look. Any ideas are welcome. I really appreciate your help.
You could use a simple UILabel subclass with edge insets, like this:
class PaddedLabel: UILabel {
var padding: UIEdgeInsets = .zero
override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: padding))
}
override var intrinsicContentSize : CGSize {
let sz = super.intrinsicContentSize
return CGSize(width: sz.width + padding.left + padding.right, height: sz.height + padding.top + padding.bottom)
}
}
Then set your custom "padding" like this:
label.padding = UIEdgeInsets(top: 0.0, left: 20.0, bottom: 0.0, right: 20.0)
Quick example:
class PaddedTestVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let defaultLabel = UILabel()
let paddedLabel = PaddedLabel()
[defaultLabel, paddedLabel].forEach { v in
v.backgroundColor = .green
v.text = "Test Text"
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
defaultLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
defaultLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
paddedLabel.topAnchor.constraint(equalTo: defaultLabel.bottomAnchor, constant: 40.0),
paddedLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
paddedLabel.padding = UIEdgeInsets(top: 12.0, left: 20.0, bottom: 12.0, right: 20.0)
}
}
The top label is a default UILabel the bottom label is a PaddedLabel - the only difference is the .padding:
A better option (in my view) would be to constrain your stack view to the margins of the cell's contentView - that way you get the defined margins (top/bottom and sides) spacing for the device and traits.

UItextfield text covers the clear button in the textfield in Swift

Hello, I'm working on a UITextField now and as the image above shows, the text covers the clear button and I want to fix that.
In this text field, I added a padding on the left side of the text and the right side of the clear button.
class testTextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func commonInit() {
let leftPadding = UIView(frame: CGRect(x: 0, y: 0, width: 17, height: 0))
leftPadding.backgroundColor = .clear
self.leftView = leftPadding
self.leftViewMode = .always
// tried adding padding to the rightView, but it hide the clear button
// let rightPadding = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 0))
// rightPadding.backgroundColor = .red
// self.rightView = rightPadding
// self.rightViewMode = .always
self.backgroundColor = .white
self.borderStyle = .none
self.layer.cornerRadius = 12
self.font = UIFont.systemFont(ofSize: 16)
self.textColor = .black
self.clearButtonMode = .whileEditing
self.isUserInteractionEnabled = true
}
override func clearButtonRect(forBounds bounds: CGRect) -> CGRect {
let originalRect = super.clearButtonRect(forBounds: bounds)
return originalRect.offsetBy(dx: -22, dy: 0)
}
// also tried changing the width of the textRext, but after I implement the following code, the textfield becomes non-clickable...
// override func textRect(forBounds bounds: CGRect) -> CGRect {
// self.bounds.size.width = bounds.size.width - 20.0;
// return bounds;
// }
// override func editingRect(forBounds bounds: CGRect) -> CGRect {
// self.bounds.size.width = bounds.size.width - 20.0;
// return bounds;
// }
}
After doing some research(like this), as I put some comments in the code, I tried adding padding to rightView and changing the textRect, but none of them worked.
I want to keep the clear button where it is now, but I want some space on the left side of the clear button, something like the image below...
Thanks to #Shadowrun, I was able to solve the issue I had.
I needed to subclass the UITextField and add editingRect. Then return the CGRect with whatever numbers I want to put. My code is something like this.
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: 17, y: 0, width: bounds.size.width - 70, height: bounds.size.height)
}

Custom Capsule Background with UIImageView programmatically not working?

I am returning a single UILabel within a UIImageView, within a Cell for a UICollectionViewController. My didSelectItem and dynamic width or working for the cell.
However I am trying to make the UIImageView a capsule shape, the below code I thought would return a circle but isn't. What I want to make is a capsule background layer for the Text, similar to a button but not a button.
class CategoryView: UIView {
private let capsuleBackground: UIImageView = {
let view = UIImageView()
view.layer.backgroundColor = UIColor.white.cgColor
view.layer.borderWidth = 1
view.layer.borderColor = COLOR_PRIMARY?.cgColor
view.layer.cornerRadius = view.frame.size.width/2
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
view.isUserInteractionEnabled = false
return view
}()
private let textLabel: CustomUILabel = {
let label = CustomUILabel(title: "Hello World")
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
fileprivate func setup() {
addSubview(capsuleBackground)
capsuleBackground.addSubview(textLabel)
capsuleBackground.centerInSuperview()
textLabel.edges(to: capsuleBackground, insets: TinyEdgeInsets(top: 8, left: 8, bottom: 8, right: 8))
}
}
this is what it looks like
view.layer.cornerRadius = 12
🤦‍♂️

Creating UIButton in Xcode with icon on right side and text on left side

I'm trying to create an UIButton in Swift where the label is 20px from left and the icon (SF Symbol) is 20px from the right side, please see the picture attached.
I created this custom class for you (when declaring a button, write Covid19Button instead of UIButton in order to use it).
class Covid19Button: UIButton {
//declare here the objects or variables you want in your custom object
var label: UILabel!
var image: UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit(){
//Set your objects' frame and other stuff like colors ecc note that here I set random dimensions. The x should be ok however. You can change them as you prefer
label = UILabel(frame: CGRect(x: 20, y: 20, width: 100, height: frame.height-40))
image = UIImageView(frame: CGRect(x: frame.width-20-image.frame.width, y: 20, width: 50, height: frame.height-40))
addSubview(label)
addSubview(image)
}
}

UILabel text truncating with wrong alignment when I add an imageView

I have a subclassed UILabel that I am going to be using for about 10 different labels of varying length (all on 1 line!) in IB. The text within each of these labels are static.
This subclassed UILabel should display an imageView to the left of the text. This imageView will hold a small icon.
It's close to working, except I'm getting odd truncating & alignment as seen from the screenshot below.
Simulator:
Storyboard setup:
Here's my code:
class ImageMarkLabel: UILabel {
var labelHeight:CGFloat = 0.0
let smileView = UIView()
let smileImageView = UIImageView()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
uiStuff()
}
func uiStuff() {
labelHeight = self.frame.height
smileView.frame = CGRect(x: 0, y: 0, width: labelHeight, height: labelHeight)
smileImageView.image = UIImage(named: "smile")
smileImageView.frame = CGRect(x: 0, y: 0, width: smileView.frame.width, height: smileView.frame.height)
}
func commonInit(){
smileView.addSubview(smileImageView)
self.addSubview(smileView)
}
override func drawText(in rect: CGRect) {
var insets = UIEdgeInsets()
if UIDevice.current.userInterfaceIdiom == .pad {
insets = UIEdgeInsets(top: 0, left: labelHeight + 10.0, bottom: 0, right: labelHeight + 10.0)
}
else {
insets = UIEdgeInsets(top: 0, left: labelHeight + 5.0, bottom: 0, right: labelHeight + 5.0)
}
super.drawText(in: rect.inset(by: insets))
}
}
Regarding Auto Layout - all of the labels are within a vertical stack view.
How can I get the text & imageView to be centered without causing any text truncating?
Change you frame in UIStuff method ,
class ImageMarkLabel: UILabel {
var labelHeight:CGFloat = 0.0
let smileView = UIView()
let smileImageView = UIImageView()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
uiStuff()
}
func uiStuff() {
labelHeight = self.frame.height
smileView.frame = CGRect(x: 0, y: 0, width: labelHeight, height: labelHeight)
smileImageView.image = UIImage(named: "smile")
smileImageView.frame = CGRect(x: 0, y: 0, width: smileView.frame.width, height: smileView.frame.height)
var insets = UIEdgeInsets()
if UIDevice.current.userInterfaceIdiom == .pad {
insets = UIEdgeInsets(top: 0, left: (labelHeight + 10.0), bottom: 0, right: (labelHeight + 10.0))
}
else {
insets = UIEdgeInsets(top: 0, left: (labelHeight + 5.0), bottom: 0, right: (labelHeight + 5.0))
}
let newRect = CGRect(origin: self.frame.origin, size: CGSize(width: self.frame.width + 2*insets.left, height: self.frame.height))
self.frame = newRect
}
func commonInit(){
smileView.addSubview(smileImageView)
self.addSubview(smileView)
}
override func drawText(in rect: CGRect) {
super.drawText(in: rect)
}
}
Output: