Programmatically moving UILabel not working - ios

I am having trouble changing the position of my UILabel. I can change font color and background etc but its position doesn't seem to move no matter what I try. Any help would be appreciated. Im also not using storyboard at all.
I'm fairly new to this so I'm probably missing something very obvious. I have googled and tried anything I thought applied but haven't had any luck.
View Builder:
import UIKit
class StandMapView: UIView {
var titleLabel: UILabel = UILabel()
var standMapImage: UIImageView = UIImageView()
var hotspotImage: UIImageView = UIImageView()
var hotspotTitleLabelArray: [UILabel] = []
var hotspotTextArray: [UITextView] = []
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func bind(standMap: StandMap, hotspots: [Hotspot]) {
titleLabel.text = standMap.title
standMapImage.image = UIImage(named: standMap.mapImage)
hotspotImage.image = UIImage(named:standMap.hotspotImage)
for hotspot in hotspots {
let hotspotTitle = UILabel()
let hotspotText = UITextView()
hotspotTitle.text = hotspot.title
hotspotText.text = hotspot.text
hotspotTitleLabelArray.append(hotspotTitle)
hotspotTextArray.append(hotspotText)
}
}
private func setupView() {
let screenWidth = UIScreen.mainScreen().bounds.width
let screenHeight = UIScreen.mainScreen().bounds.height
self.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
standMapImage.translatesAutoresizingMaskIntoConstraints = false
hotspotImage.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor.blackColor()
titleLabel.sizeToFit()
titleLabel.frame = CGRect(x: screenWidth/2, y: 30, width: 0, height: 0)
titleLabel.textAlignment = .Center
titleLabel.numberOfLines = 0
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.textColor = UIColor.whiteColor()
addSubview(titleLabel)
}
}
View Controller:
import UIKit
class StandMapViewController: UIViewController {
var standMap: StandMap!
var hotspots: [Hotspot] = []
override func viewDidLoad() {
super.viewDidLoad()
Hotspot.all { hotspot in
hotspot.forEach(self.assignHotspotVariable)
}
StandMap.build {standMap in
standMap.forEach(self.assignStandMapVariable)
}
viewForStandMap(standMap, hotspots: hotspots)
}
private func assignStandMapVariable(standMap: StandMap) {
self.standMap = standMap
}
private func assignHotspotVariable(hotspot: Hotspot) {
hotspots.append(hotspot)
}
private func viewForStandMap(standMap: StandMap, hotspots: [Hotspot]) {
let standMapView = StandMapView(frame: CGRectZero)
standMapView.bind(standMap, hotspots: hotspots)
view.addSubview(standMapView)
}
}

If you want to change the position of the label, you need to change the origin x and y
titleLabel.frame.origin.x = 0.0 // put your value
titleLabel.frame.origin.y = 0.0 // put your value
self.view.layoutIfNeeded()

I managed to solve this using snapkit cocoa pod to make the constraints and then adding the subview before declaring these constraints.
Thanks for everyones help.
Heres the changes i made to the setupView function:
private func setupView() {
titleLabel.translatesAutoresizingMaskIntoConstraints = false
standMapImage.translatesAutoresizingMaskIntoConstraints = false
hotspotImage.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor.blackColor()
titleLabel.textColor = UIColor.whiteColor()
titleLabel.textAlignment = .Center
titleLabel.numberOfLines = 0
titleLabel.adjustsFontSizeToFitWidth = true
addSubview(titleLabel)
titleLabel.snp_makeConstraints { make in
make.topMargin.equalTo(snp_topMargin).multipliedBy(60)
make.centerX.equalTo(snp_centerX)
}
}

If your label has constraints with Autolayout in storyboard, you must disable constraints to move the frame. Try using
titleLabel.translatesAutoresizingMaskIntoConstraints = YES;
Hope this may solve your issue.

If you are using AutoLayout, do following:
set outlet for constraint of UILable that you want to change.
Then change constant of that constraint as per your need.
e.g: xPosOfLable.constant = x

Related

Can't hide the top and bottom lines in custom searchBar

I´ve tried to change background color inside class SearchBarView: UIView {}:
searchBar.searchTextField.backgroundColor = .clear
searchBar.backgroundColor = .clear
and tryed something like that inside MainViewController:
searchBar.searchTextField.backgroundColor = .clear
searchBar.backgroundColor = .clear
searchBar.layer.backgroundColor = UIColor.clear.cgColor
but, unfortunately I still see this lines inside my custom searchBar.
How can I get rid of these lines?
My SearchBarView class:
class SearchBarView: UIView {
lazy var searchBar = createSearchBar()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(searchBar)
searchBar.snp.makeConstraints { make in
make.leading.equalTo(32)
make.centerY.equalToSuperview()
make.height.equalTo(34)
make.width.equalTo(300)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
fileprivate extension SearchBarView {
private func createSearchBar() -> UISearchBar {
let searchBar = UISearchBar()
searchBar.placeholder = " Search"
searchBar.searchTextField.font = UIFont(name: "MarkPro", size: 15)
searchBar.searchTextField.backgroundColor = .clear
let textFieldInsideSearchBar = searchBar.value(forKey: "searchField") as? UITextField
let imageV = textFieldInsideSearchBar?.leftView as! UIImageView
imageV.image = imageV.image?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
imageV.tintColor = UIColor(hexString: "FF6E4E")
return searchBar
}
}
My MainViewController class:
class MainViewController: UIViewController {
private var searchBarView: SearchBarView!
override func viewDidLoad() {
super.viewDidLoad()
setupSearchBarView()
}
private func setupSearchBarView() {
searchBarView = SearchBarView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
view.addSubview(searchBarView)
searchBarView.searchBar.clipsToBounds = true
searchBarView.searchBar.layer.cornerRadius = 17
searchBarView.searchBar.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
searchBarView.searchBar.searchTextField.clipsToBounds = true
let directionalMargins = NSDirectionalEdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 0)
searchBarView.searchBar.directionalLayoutMargins = directionalMargins
searchBarView.snp.makeConstraints { make in
make.leading.equalToSuperview()
make.top.equalTo(categoriesView.snp.bottom)
make.trailing.equalToSuperview()
make.height.equalTo(60)
}
}
}
If you want to make the top and bottom border lines on the textfield disappear (the dark gray ones), you will want to tweak the text field's border properties rather than the background colors. Try something like this:
searchBar.searchTextField.layer.borderWidth = 0
or
searchBar.searchTextField.layer.borderColor = UIColor.clear.cgColor
and adapt it to fit how you've set up the relevant subviews in your custom search bar.
Set the searchBar background image to empty. This eliminates all background issues you may have such as unwanted lines. For more info reference Apple docs: https://developer.apple.com/documentation/uikit/uisearchbar/1624276-backgroundimage
searchBar.backgroundImage = UIImage()

Add UILabel as subview of UITextField on top

I am in the process of implementing a UILabel as a subview of a UITextField which will be shown right above the UITextField itself. The UITextField has a rounded border and what I would like to achieve is the UILabel to be shown over the border.
Everything currently works as expected, but the UILabel is drawn behind the border of the UITextField. I want it to go "over" (above) the border so the white backgroundColor would be shown above part of the border and make the text more easily readible.
var priceTextField: CustomTextField = {
let priceTextField = CustomTextField()
priceTextField.layer.cornerRadius = 10.0
priceTextField.layer.borderWidth = 1.0
priceTextField.layer.borderColor = UIColor.darkGray.cgColor
priceTextField.translatesAutoresizingMaskIntoConstraints = false
priceTextField.font = UIFont.systemFont(ofSize: 15)
priceTextField.textColor = .black
priceTextField.text = "0"
priceTextField.suffix = "EUR"
priceTextField.suffixTextColor = .darkGray
priceTextField.suffixSpacing = 2.0
priceTextField.textAlignment = .center
priceTextField.labelText = "Price"
return priceTextField
}()
In my CustomTextField class (subclass of UITextField):
public var labelText: String?
var topLabel: UILabel = {
let topLabel = UILabel()
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.textAlignment = .center
topLabel.font = UIFont.systemFont(ofSize: 12)
topLabel.textColor = .lightGray
topLabel.backgroundColor = .white
topLabel.numberOfLines = 1
return topLabel
}()
func setupLabel() {
self.addSubview(topLabel)
topLabel.centerYAnchor.constraint(equalTo: self.topAnchor).isActive = true
topLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
topLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20).isActive = true
topLabel.text = labelText
}
I call setupLabel() at the end of the draw(_ rect: CGRect) method of UITextField (because I work with this to show the EUR sign always behind the entered value).
I have tried to play around with bringSubviewToFront and changing the zPosition of the layer of the UILabel, without success.
It now looks like this:
How can I bring the text "above" the border on the top?
EDIT: Tried Sh_Khan's solution, but it's still hidden behind the border.
import Foundation
import UIKit
public class CustomTextView: UIView, UITextFieldDelegate {
public var labelText: String?
var customTextField: CustomTextField = {
let customTextField = CustomTextField()
customTextField.translatesAutoresizingMaskIntoConstraints = false
customTextField.font = UIFont.systemFont(ofSize: 15)
customTextField.textColor = .black
customTextField.textAlignment = .center
customTextField.text = "0"
customTextField.suffix = "EUR"
customTextField.suffixTextColor = .lightGray
customTextField.suffixSpacing = 2.0
return customTextField
}()
var topLabel: UILabel = {
let topLabel = UILabel()
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.font = UIFont.systemFont(ofSize: 12)
topLabel.textColor = .darkGray
topLabel.numberOfLines = 1
topLabel.backgroundColor = .red
topLabel.textAlignment = .center
return topLabel
}()
override public init(frame: CGRect) {
super.init(frame: frame)
setupBorders()
}
public override func layoutSubviews() {
setupViews()
}
func setupBorders() {
self.layer.cornerRadius = 10.0
self.layer.borderColor = UIColor.lightGray.cgColor
self.layer.borderWidth = 1.0
}
func setupViews() {
addSubview(topLabel)
// insertSubview(topLabel, aboveSubview: customTextField)
insertSubview(customTextField, belowSubview: topLabel)
customTextField.topAnchor.constraint(equalTo: topAnchor).isActive = true
customTextField.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
customTextField.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
customTextField.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
topLabel.centerYAnchor.constraint(equalTo: topAnchor).isActive = true
topLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
topLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
topLabel.text = labelText
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupViews()
}
}
You can try to organize it by creating a UIView subclass , so everything appear properly in it's order of adding
class CustomView: UIView {
var priceTextField: CustomTextField = {
let priceTextField = CustomTextField()
priceTextField.layer.cornerRadius = 10.0
priceTextField.layer.borderWidth = 1.0
priceTextField.layer.borderColor = UIColor.darkGray.cgColor
priceTextField.translatesAutoresizingMaskIntoConstraints = false
priceTextField.font = UIFont.systemFont(ofSize: 15)
priceTextField.textColor = .black
priceTextField.text = "0"
priceTextField.suffix = "EUR"
priceTextField.suffixTextColor = .darkGray
priceTextField.suffixSpacing = 2.0
priceTextField.textAlignment = .center
priceTextField.labelText = "Price"
return priceTextField
}()
var topLabel: UILabel = {
let topLabel = UILabel()
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.textAlignment = .center
topLabel.font = UIFont.systemFont(ofSize: 12)
topLabel.textColor = .lightGray
topLabel.backgroundColor = .white
topLabel.numberOfLines = 1
return topLabel
}()
var lableStr:String?
init(frame: CGRect,lblTex:String) {
super.init(frame: frame)
lableStr = lblTex
createSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
createSubviews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
createSubviews()
}
func createSubviews() {
// all the layout code from above
// add the textfield then the label and set constraints properly
}
}
According to the Apple specification: It is composited above the receiver’s contents and sublayers.
So, the border will always be above all subviews, even if one brings the subview to the front and so on.
So one needs to make a background view to fake the border.
similar to Stackoverflow Question
Example:
Here self is "TextField"
activeborderView is "UiView"
activeborderView.frame = CGRect.init(x: -1, y: -1, width: self.frame.size.width+2, height: self.frame.size.height+2)
activeborderView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(activeborderView)
activeborderView.topAnchor.constraint(equalTo: self.topAnchor, constant:-1).isActive = true // Place our label 10 pts above the text field
activeborderView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: -1).isActive=true
activeborderView.heightAnchor.constraint(equalToConstant: self.frame.size.height+2).isActive=true
activeborderView.widthAnchor.constraint(equalToConstant: self.frame.size.width+2).isActive=true
activeborderView.layer.borderWidth = 3
activeborderView.layer.borderColor = CustomColor.blue().cgColor
activeborderView.layer.cornerRadius = 5
activeborderView.backgroundColor = .white
self.sendSubviewToBack(activeborderView)
self.setNeedsDisplay()

Swift: Make vertical scrolling feed with programmatically sized UIViews

I'm building a scrolling feed in my app with data grabbed from a database (firebase). I'm not very experienced in Swift as most of the stuff I do is web design. What I'm looking for is a good way to size the height of my UIViews. Here is what I currently have (fixed height):
Here's my code UIView class:
class eventView: UIView {
let eventDate : UILabel = {
let eventDate = UILabel()
eventDate.translatesAutoresizingMaskIntoConstraints = false
eventDate.numberOfLines = 0
eventDate.textAlignment = .center
return eventDate
}()
let eventTitle : UILabel = {
Same thing as eventDate
}()
let eventDesc : UILabel = {
same thing as eventDate
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.lightGray
self.layer.cornerRadius = 10
self.addSubview(eventDate)
eventDate.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
eventDate.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
self.addSubview(eventTitle)
eventTitle.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
eventTitle.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
eventTitle.topAnchor.constraint(equalTo: eventDate.bottomAnchor).isActive = true
self.addSubview(eventDesc)
eventDesc.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
eventDesc.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
eventDesc.topAnchor.constraint(equalTo: eventTitle.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is my for list that displays these UIViews:
var i = 0
for data in self.eventViewData {
let view = eventView(frame: CGRect(x: 0, y: ( CGFloat(170 * i)), width: self.scrollView.frame.width - 20, height: CGFloat(150)))
view.center.x = self.scrollView.center.x
view.eventDate.text = data.date
view.eventTitle.text = data.title
view.eventDesc.text = data.description
self.scrollView.addSubview(view)
i += 1
}
I usually am using HTML div's and such so I'm having a hard time figuring out how to style these. Any information or links to tutorials on how to programmatically adjust constraints to the UILabels in my eventViews are also appreciated.
How to calculate the desired height seems to be the question. While there are a lot of ways to do this one approach would be something like this:
func heightForView(text:String, font:UIFont, width:CGFloat) -> CGFloat{
let label:UILabel = UILabel(frame: CGRectMake(0, 0, width, CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
let font = UIFont(name: "Helvetica", size: 20.0)
var height = heightForView("This is just a load of text", font: font, width: 100.0)
// apply height to desired view
I prefer to put the heightForView function in an extension myself but it isn't required.
I assumed you're using table view, why not do dynamic height? Good reference https://www.raywenderlich.com/1067-self-sizing-table-view-cells
In essense, you use auto layout to define your top and bottom constraint accordingly for every element in your cell, and your table view delegation/data source method for heightforrow and estimatedheightforrow, return UITableViewAutomaticDimension

Custom UITextField with UILabel multiline support for error text

I want to create custom UITextField with error label on bottom of it. I want the label to be multiline, I tried numberOfLines = 0. But it is not working.
Here is my snippet for the class
public class MyTextField: UITextField {
private let helperTextLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 12.0, weight: UIFont.Weight.regular)
label.textColor = helperTextColor
label.numberOfLines = 0
return label
}()
public override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(errorTextLabel)
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addSubview(errorTextLabel)
}
public override func layoutSubviews() {
super.layoutSubviews()
errorTextLabel.frame = CGRect(x: bounds.minX, y: bounds.maxY - 20, width: bounds.width, height: 20)
}
public override var intrinsicContentSize: CGSize {
return CGSize(width: 240.0, height: 68.0)
}
public override func sizeThatFits(_ size: CGSize) -> CGSize {
return intrinsicContentSize
}
}
I think the root cause is because I set height to 20, but how can I set the height dynamically based on the errorTextLabel.text value?
You are giving your label a fixed size.
Not using AutoLayout and giving the textfield and label room to expand it's size when needed.
Personally, if creating this particular control, I would create a UIView with a textfield and a label inside a UIStackView. That way if the label is hidden when there is no error the stackview will automatically adjust the height for you. Then when you unhide it, the view will expand to fit both controls.
A basic example:
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
class LabelledTextView: UIView {
private let label = UILabel()
private let textfield = UITextField()
private let stackView = UIStackView()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
stackView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
stackView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
stackView.alignment = .leading
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.addArrangedSubview(textfield)
stackView.addArrangedSubview(label)
textfield.placeholder = "Please enter some text"
label.numberOfLines = 0
label.text = "Text did not pass validation, Text did not pass validation, Text did not pass validation, Text did not pass validation"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let labelledTextView = LabelledTextView(frame: CGRect(x: 50, y: 300, width: 300, height: 60))
let vc = UIViewController()
vc.view.addSubview(labelledTextView)
labelledTextView.translatesAutoresizingMaskIntoConstraints = false
labelledTextView.topAnchor.constraint(equalTo: vc.view.topAnchor).isActive = true
labelledTextView.leftAnchor.constraint(equalTo: vc.view.leftAnchor).isActive = true
labelledTextView.widthAnchor.constraint(equalToConstant: 300).isActive = true
labelledTextView.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
PlaygroundPage.current.liveView = vc.view
You need to use sizeToFit():
errorTextLabel.sizeToFit()

inputAccessoryView with intrinsicContentSize in iphone x not working

please do not down vote will try my best to explain problem.
i have an InputAccessoryView which was working fine before iphone x, in iphone x my inputAssesoryView is showing at very bottom below the layoutguide. i found following solution
inputAccessoryView Iphone X
after following idea from above link i can place my textfield above the layout guid but it become unresponsive. which is happening because there is no frame size define for view of inputAccessoryView.
class ChatInputContainerView: UIView, UITextFieldDelegate {
override var intrinsicContentSize: CGSize {
return CGSize.zero
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var inputTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Enter message..."
textField.translatesAutoresizingMaskIntoConstraints = false
textField.delegate = self
return textField
}()
override init(frame: CGRect) {
super.init(frame: frame)
autoresizingMask = .flexibleHeight
self.inputTextField.leftAnchor.constraint(equalTo: rightAnchor, constant: 8).isActive = true
self.inputTextField.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor).isActive = true
self.inputTextField.rightAnchor.constraint(equalTo: leftAnchor).isActive = true
self.inputTextField.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
}
}
following is controller where i am using inputAccessory View
class chatController: UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
lazy var inputContainerView: ChatInputContainerView = {
// let chatInputContainerView = ChatInputContainerView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50))
let chatInputContainerView = ChatInputContainerView()
chatInputContainerView.chatLogController = self
return chatInputContainerView
}()
override var inputAccessoryView: UIView? {
get {
return inputContainerView
}
}
override var canBecomeFirstResponder : Bool {
return true
}
}
previously i was using following code to get size of InputAccessoryView which place the view at the bottom with fix height.
let chatInputContainerView = ChatInputContainerView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50))
but now i am using following to set inputAccessoryView which is not working properly, textfield inside view become unresponsive because parent view have no size define.
override var intrinsicContentSize: CGSize {
return CGSize.zero
}
autoresizingMask = .flexibleHeight
in following image you can see all my control are now above the safeAreaLayout but they become unresponsive.
sample image
let me know if you do not understand. please help thank you in advance.
Try adding a top anchor to your 'inputTextField'
self.inputTextField.topAnchor.constraint(equalTo: topAnchor).isActive = true
I might be wrong but without knowing where the top is the 'ChatInputContainerView' will have zero height.

Resources