0

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()

    }

}
PennyWise
  • 595
  • 2
  • 12
  • 37
  • `bringSubviewToFront ` in `viewdidlayoutSubViews` may help – Lal Krishna Apr 20 '19 at 15:07
  • @Sh_Khan Interesting. I started out with a simple `UITextField` and wanted to add a `UILabel` on top rather than on the left. So you mean, create a `UIView` with the `UITextField` and `UILabel` added to this `UIView`? Would that solve my problem? – PennyWise Apr 20 '19 at 15:08
  • @LalKrishna I tried that indeed. Did not work. – PennyWise Apr 20 '19 at 15:09
  • The main problem is that the UILabel has another subview that you're unaware of, its _content view_. This view is ending up in front of your topLabel. But another problem is that `drawRect` is the wrong place to do this. The only thing that you should do there is draw. Just add this as a proper view in the normal way. – matt Apr 20 '19 at 15:20

2 Answers2

1

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
    }
}
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
  • I did this, by creating a UIView and adding both elements, setting up the views, but it still shows up behind the border of the `UITextField`. I'll update and add my code to the post. – PennyWise Apr 20 '19 at 15:34
  • remove `layoutSubviews` method no need for it – Shehata Gamal Apr 20 '19 at 15:38
  • I first didn't use layoutSubviews, and called setupViews() after init, making the UILabel invisible (I guess the text isn't set then). – PennyWise Apr 20 '19 at 15:40
  • Don't add the border to the customView , make it for the textfield only – Shehata Gamal Apr 20 '19 at 15:40
  • look carefully to answer `setupViews` is called from init frame and init decoder – Shehata Gamal Apr 20 '19 at 15:41
  • The label shows up now, and above the border! However I am unable to set the text to `labelText` (a var I declared so I can access it from inside every CustomTextField(). Where should I call `topLabel.text = labelText`? EDIT: Just added it inside layoutSubviews() and this works perfectly! – PennyWise Apr 20 '19 at 15:44
  • `layoutSubviews` isn't the correct place , you can create a custom init see edit – Shehata Gamal Apr 20 '19 at 15:49
0

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()

enter image description here