I am dynamically adding views in the UiStackview. Since UiStackview is not a regular view, SO I can not add a bottom border to it. That is why I have planned to add a UILabel at the end of it
The label that I will add at the end of my UIStackview will be dealt as a border. I was thinking to make its height as 1point. and give it a background color. And expand its height to full width of screen.
But its height is not getting controlled. Can anyone tell me what the problem is?
Here is the little code snippet
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 10, height: 1))
label.backgroundColor = UIColor.black
label.text = ""
bottomBorder.addArrangedSubview(label)
I am adding this in the end of the main stackview. it gets added in the main stackview but with the height of I think 30 point. Or may be its the default height of the UiLabel
My questions are:
How to add UiLabel or a border at the end of stackview (vertical alignment)
Is there any way that I can add border to my stackview directly? Four side border or at least bottom border?
You have 2 alternatives to achieve this:
1- put the stackview inside a UIView parentView
2- do not add UILabel directly to the UIStackView, add the UILabel to UIView then add the UIView to the UIStackView, so that you can whatever separators you want to the UIView.
Code to do that:
override func viewDidLoad()
{
super.viewDidLoad()
stackView.distribution = .fillEqually
let v1 = getViewForStackView(lblText: "lbl1")
let v2 = getViewForStackView(lblText: "lbl2")
stackView.addArrangedSubview(v1)
stackView.addArrangedSubview(v2)
}
func getViewForStackView(lblText:String)->UIView
{
let rectView = CGRect(x: 0, y: 0, width: 100, height: 100)
let view = UIView(frame: rectView)
view.backgroundColor = .green
let label = UILabel()
label.text = lblText
label.backgroundColor = .red
addFillingSubview(parentView: view, subview: label, insets: UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8))
return view
}
func addFillingSubview(parentView:UIView, subview: UIView, insets: UIEdgeInsets = .zero)
{
subview.translatesAutoresizingMaskIntoConstraints = false
parentView.addSubview(subview)
let views = ["subview": subview]
let metrics = ["top": insets.top, "left": insets.left, "bottom": insets.bottom, "right": insets.right]
parentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|-(left)-[subview]-(right)-|", options: [], metrics: metrics, views: views))
parentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(top)-[subview]-(bottom)-|", options: [], metrics: metrics, views: views))
}
Output:
Use this class
class BorderedStackView: UIStackView {
let borderWidth : Int = 2
let borderColor : UIColor = UIColor.darkGray;
var borderView : UIView!
required init(coder: NSCoder) {
super.init(coder: coder);
initializeSubviews();
}
required override init(frame: CGRect) {
super.init(frame: frame);
initializeSubviews();
}
func initializeSubviews() {
borderView = UIView.init();
borderView.backgroundColor = UIColor.black;
self.addSubview(borderView);
}
override func layoutSubviews() {
super.layoutSubviews();
var frame = self.bounds;
frame.origin.y = frame.size.height - CGFloat.init(borderWidth)
frame.size.height = CGFloat.init(borderWidth);
self.borderView.frame = frame;
self.bringSubview(toFront: self.borderView)
}
}
Uses:
let stackView = BorderedStackView.init(frame: CGRect.init(x: 50, y: 50, width: 200, height: 200));
stackView.axis = .vertical
stackView.borderWidth = 20;
stackView.borderColor = .red;
self.view.addSubview(stackView);
let subView1 = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 200, height: 100));
subView1.backgroundColor = UIColor.red;
stackView.addSubview(subView1);
let subView2 = UIView.init(frame: CGRect.init(x: 0, y: 100, width: 200, height: 100));
subView2.backgroundColor = UIColor.black;
stackView.addSubview(subView2);
Related
This question already has answers here:
Add padding between label and its border
(4 answers)
Closed 8 months ago.
I have created a text programmatically with a grey background using UILabel.
Now I would like to add padding to this paragraph/text. Also, it would be great if you could show me how to add margin to my UILabel as well.
import UIKit
final class SignUpViewController: UIViewController {
public let identifier = "Sign Up"
private let logoImage : UIImageView = {
let imageView = UIImageView()
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "MyLogoWithTitle")
imageView.clipsToBounds = true
return imageView
}()
private let instructionText : UILabel = {
let label = UILabel()
label.text = "Please read terms and conditions below carefully before proceeding with the registration."
label.backgroundColor = UIColor().colorFromHex(hex: "#2C333C", opacity: 0.4)
label.numberOfLines = 0
label.tintColor = .white
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.addSubview(logoImage)
view.addSubview(instructionText)
view.backgroundColor = UIColor().colorFromHex(hex: "#141920", opacity: 1.0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
logoImage.frame = CGRect(x: 0,
y: 0,
width: 140,
height: 60)
logoImage.center = CGPoint(x: view.center.x, y: view.height/5)
instructionText.frame = CGRect(
x: 5,
y: 5 + logoImage.bottom,
width: view.width - 20,
height: 50)
.integral
instructionText.layer.cornerRadius = 10
}
}
Notice that I created an extension to UIColor so that I can input hex color in this way - UIColor().colorFromHex(hex: "#2C333C", opacity: 0.4) .
I am looking forward to hearing from you. Thank you.
You can insert this UILabel into the container (any UIView) and set its position inside.
But the simplest trick is to use UIButton instead of UILabel. You can configure UIEdgeInsets for padding.
So that UIButton does not act as a button simply set button.isUserInteractionEnabled = false.
By default, text in the button are placed in the center, but its position is easy to change with contentHorizontalAlignment and contentVerticalAlignment
And as a bonus, you can add icons right near to the text. :)
UPD.
Could you give me a simple example? I tried that way but I didn't get the result I expected. – Punreach Rany
let buttonUsedAsLabel = UIButton()
// Your question was about padding
// It's it!
buttonUsedAsLabel.titleEdgeInsets = UIEdgeInsets(top: 5, left: 20, bottom: 5, right: 20)
// Make it not user interactable as UILabel is
buttonUsedAsLabel.isUserInteractionEnabled = false
// set any other properties
buttonUsedAsLabel.setTitleColor(.white, for: .normal)
buttonUsedAsLabel.contentVerticalAlignment = .top
buttonUsedAsLabel.contentHorizontalAlignment = .left
// Set title propeties AFTER it was created with text because it's nullable
// You can use attributed title also
// Never use any (button.titleLabel) before its creation
// for example: (button.titleLabel?.text = "zzz") do nothing here
buttonUsedAsLabel.setTitle("This is the text", for: .normal)
buttonUsedAsLabel.titleLabel?.font = .systemFont(ofSize: 20, weight: .medium)
buttonUsedAsLabel.titleLabel?.numberOfLines = 0
buttonUsedAsLabel.titleLabel?.lineBreakMode = .byWordWrapping
// and so on
// ...
// This is the triсk :)
Of course, you can do it with a storyboard if prefer.
1. Add this class
PaddingLabel.swift
import UIKit
class PaddingLabel: UILabel {
var edgeInset: UIEdgeInsets = .zero
override func drawText(in rect: CGRect) {
let insets = UIEdgeInsets.init(top: edgeInset.top, left: edgeInset.left, bottom: edgeInset.bottom, right: edgeInset.right)
super.drawText(in: rect.inset(by: insets))
}
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(width: size.width + edgeInset.left + edgeInset.right, height: size.height + edgeInset.top + edgeInset.bottom)
}
}
2. Add this code to your ViewController
let label = PaddingLabel()
override func viewDidLoad() {
super.viewDidLoad()
label.backgroundColor = UIColor().colorFromHex(hex: "#2C333C", opacity: 0.4)
//Setting the padding label
label.edgeInset = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)
}
The answer to the link below is that I wrote the same content based on the storyboard.
Add padding between label and its border
I use textfield. Set padding and text in textfield. And do not allow editing.
extension UITextField {
func addLeftPadding() {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: self.frame.height))
self.leftView = paddingView
self.leftViewMode = ViewMode.always
}
}
//ViewController
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.addLeftPadding()
myTextField.isUserInteractionEnabled = false
myTextField.text = "your label text"
}
I'm adding a header view to my UITableView and want to add a subview to it having some margins.
class ViewController: UIViewController {
private let tablewView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
tablewView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tablewView)
[
tablewView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
tablewView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
tablewView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tablewView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
].forEach{ $0.isActive = true}
let headerView = UIView(frame: CGRect(x: 0, y: 120, width: 200, height: 100))
headerView.backgroundColor = .blue
let subView = UIView(frame: CGRect(x: 10, y: 10, width: 180, height: 80))
subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
subView.backgroundColor = .yellow
headerView.addSubview(subView)
tablewView.tableHeaderView = headerView
}
}
The problem is that the right margin isn't preserved when the header is resized (when the table view is laid out). As you can see on the image, the right margin is missing:
If I'm using the same view without UITableView, then the margin is preserved as expected.
Is it a UIKit bug? Are there any workarounds?
I know that I can try AutoLayout solutions from here Is it possible to use AutoLayout with UITableView's tableHeaderView? but they're looking a bit hacky. autoresizingMask is supposed to work, after all.
In Cocoa programming as in comedy, timing is everything.
Add the subview in a one-time implementation of viewDidLayoutSubviews and all will be well. The subview will appear correctly, and will continue working if the table view is resized (e.g. due to rotation of the interface).
So, cut these four lines:
let subView = UIView(frame: CGRect(x: 10, y: 10, width: 180, height: 80))
subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
subView.backgroundColor = .yellow
headerView.addSubview(subView)
And instead:
var didLayout = false
override func viewDidLayoutSubviews() {
guard !didLayout else { return }
didLayout.toggle()
if let h = tablewView.tableHeaderView {
let subView = UIView(frame: h.bounds.insetBy(dx: 10, dy: 10))
subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
subView.backgroundColor = .yellow
h.addSubview(subView)
}
}
In my textfield there is a right view and datepicker added. When I select the date, text is overlapping with the right view.
I am using a Designable class for adding right view. How can I fix overlapping issue
Here is the code for setting up rightview
rightViewMode = UITextField.ViewMode.always
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 17, height: 17))
imageView.contentMode = .scaleAspectFit
imageView.image = rightImage
imageView.tintColor = color
// Added containerView for repositioning image
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 20))
self.addSubview(containerView)
containerView.addSubview(imageView)
rightView = containerView
Without seeing all your code it's kind of hard to tell what is going on here. Do you use storyboards and constraints? Or do you hardcode all the frames of your views?
What I would do here, is using a UIStackView and constraints, because it helps getting rid of all the hardcoded positioning values, and it gives you much more flexibility for laying out your UI.
let textField = UITextField()
textField.placeholder = "16 December 2018"
let imageView = UIImageView()
imageView.backgroundColor = .systemBlue
let stackView = UIStackView(arrangedSubviews: [textField, imageView])
stackView.axis = .horizontal
stackView.distribution = .fill
stackView.alignment = .center
stackView.spacing = 10
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 17).isActive = true
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor).isActive = true
As you can see I set the stack view's distribution property to .fill here, so because the width of your image is constrained to 17, your text field width will adjust to fill the width of the stack view. You may want to adjust this property, and the spacing property, depending on what kind of behaviour you're looking for.
I have solved my problem with this peace of code:
Added Padding For Textfields
let padding = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
Trying to make scrollView with elements created in code but i got overlapping this elements on each other. Scrollview itself made in storyboard. Here is my code:
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView! {
didSet {
scrollView.backgroundColor = .yellow
}
}
lazy var im: UIImageView = {
let im = UIImageView(frame: CGRect(x: 10, y: 74, width: self.view.frame.size.width - 20, height: 200))
im.backgroundColor = .white
return im
}()
lazy var label: UILabel = {
let l = UILabel(frame: CGRect(x: 10, y: 284, width: self.view.frame.size.width - 20, height: CGFloat.greatestFiniteMagnitude))
l.numberOfLines = 0
l.lineBreakMode = .byWordWrapping
l.text = "crazy amount of text"
l.sizeToFit()
self.scrollView.contentSize = CGSize(width: self.view.frame.width, height: l.frame.height + 280)
return l
}()
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.addSubview(im)
self.scrollView.addSubview(label)
}
}
White rectangle is imageView!
How to prevent overlapping these elements?
You can change the code of lazy variable im to
lazy var im: UIImageView = {
let im = UIImageView(frame: CGRect(x: 10, y: self.label.frame.height, width: self.view.frame.size.width - 20, height: 200))
im.backgroundColor = .white
return im
}()
For image to be on top you can add l.frame.y = self.im.frame.height after l.sizeToFit()
lazy var label: UILabel = {
let l = UILabel(frame: CGRect(x: 10, y: 200, width: self.view.frame.size.width - 20, height: CGFloat.greatestFiniteMagnitude))
l.numberOfLines = 0
l.lineBreakMode = .byWordWrapping
l.text = "Big Text"
l.sizeToFit()
l.frame.y = self.im.frame.height
self.scrollView.contentSize = CGSize(width: self.view.frame.width, height: l.frame.height + 280)
return l
}()
This will give you the desired result.
Hope it helps!
I found solution using Visual Formatting Language. Here I put fully working no-warning code:
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView! {
didSet {
scrollView.backgroundColor = .yellow
}
}
lazy var label: UILabel = {
let l = UILabel(frame: .zero)
l.numberOfLines = 0
l.lineBreakMode = .byWordWrapping
l.translatesAutoresizingMaskIntoConstraints = false
l.text = "REALLY HUGE TEXT"
l.sizeToFit()
return l
}()
lazy var im: UIImageView = {
let im = UIImageView(frame: .zero)
im.backgroundColor = .white
im.translatesAutoresizingMaskIntoConstraints = false
return im
}()
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.addSubview(im)
self.scrollView.addSubview(label)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
configureSizes()
}
fileprivate func configureSizes() {
let metrics = ["elementWidth" : self.view.frame.size.width - 20, "imageHeight" : 220]
scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-10-[v1(imageHeight)]-10-[v2]-10-|", options: [], metrics: metrics, views: ["v1" : im, "v2" : label]))
scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[v1(elementWidth)]-10-|", options: [], metrics: metrics, views: ["v1" : im]))
scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[v2(elementWidth)]-10-|", options: [], metrics: metrics, views: ["v2" : label]))
}
}
Here you will see blank white rectangle on the top - imageView and after imageView you will find multiline label that stretches content size of scroll view if it is needed.
Hope somebody will find this helpful and save you hours :)
I am trying to access the MyCustomView from another class using the following code in ViewController.swift ..
var view = MyCustomView(frame: CGRectZero)
.. in the viewDidLoad method. The problem is the view does not get initialized in the simulator.
I have already set class in storyboard for the current ViewController.
class MyCustomView: UIView {
var label: UILabel = UILabel()
var myNames = ["dipen","laxu","anis","aakash","santosh","raaa","ggdds","house"]
override init(){
super.init()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.addCustomView()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addCustomView() {
label.frame = CGRectMake(50, 10, 200, 100)
label.backgroundColor=UIColor.whiteColor()
label.textAlignment = NSTextAlignment.Center
label.text = "test label"
label.hidden=true
self.addSubview(label)
var btn: UIButton = UIButton()
btn.frame=CGRectMake(50, 120, 200, 100)
btn.backgroundColor=UIColor.redColor()
btn.setTitle("button", forState: UIControlState.Normal)
btn.addTarget(self, action: "changeLabel", forControlEvents: UIControlEvents.TouchUpInside)
self.addSubview(btn)
var txtField : UITextField = UITextField()
txtField.frame = CGRectMake(50, 250, 100,50)
txtField.backgroundColor = UIColor.grayColor()
self.addSubview(txtField)
}
The CGRectZero constant is equal to a rectangle at position (0,0) with zero width and height. This is fine to use, and actually preferred, if you use AutoLayout, since AutoLayout will then properly place the view.
But, I expect you do not use AutoLayout. So the most simple solution is to specify the size of the custom view by providing a frame explicitly:
customView = MyCustomView(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
self.view.addSubview(customView)
Note that you also need to use addSubview otherwise your view is not added to the view hierarchy.
Swift 3 / Swift 4 Update:
let screenSize: CGRect = UIScreen.main.bounds
let myView = UIView(frame: CGRect(x: 0, y: 0, width: screenSize.width - 10, height: 10))
self.view.addSubview(myView)
var customView = UIView()
#IBAction func drawView(_ sender: AnyObject) {
customView.frame = CGRect.init(x: 0, y: 0, width: 100, height: 200)
customView.backgroundColor = UIColor.black //give color to the view
customView.center = self.view.center
self.view.addSubview(customView)
}
let viewDemo = UIView()
viewDemo.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
self.view.addSubview(viewDemo)
view = MyCustomView(frame: CGRectZero)
In this line you are trying to set empty rect for your custom view. That's why you cant see your view in simulator.