Search code examples
ioslayoutuiviewuilabelswift4

Swift - How to set custom view by text length


Here is my code,

func bannerNotification(text: String){
    let container = UIView()
    let image = UIImageView()
    let label = UILabel()
    container.frame = CGRect(x: 0, y:0, width: self.view.frame.size.width, height: 100)
    container.backgroundColor = .blue
    image.frame = CGRect(x: 15, y: 50, width: 30, height: 30)
    image.image = UIImage(named: "passport")
    label.frame = CGRect(x: image.bounds.maxX + 35, y: 50, width: container.frame.size.width - 100, height: 50)
    label.backgroundColor = .red
    label.numberOfLines = 0
    label.font = UIFont(name:"Helvetica Neue", size: 15)
    label.text = text
    container.addSubview(image)
    container.addSubview(label)
    self.view.addSubview(container)
}

According to this code the container and Image is coming on right position but if I pass small text so my text is not inline with Image, means my image top position and text top position should be same.

If I will pass a big text so container bottom and Label bottom should be same and all text should be there not truncated and Image and Label should be inline from the top.

Expected Behavior

Real Behavior


Solution

  • You really want to use auto-layout for this, particularly since you're using multi-line label -- meaning, it will vary in height.

    Here's an example - read through the comments to understand the auto-layout constraints:

    class KingViewController: UIViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            bannerNotification(text: "Banner Test")
            //bannerNotification(text: "Banner Test with lots of text to wrap onto multiple lines. Of the many advantages with using auto-layout, notice that the banner will stretch when you rotate the device.")
        }
    
        func bannerNotification(text: String){
            let container = UIView()
            let image = UIImageView()
            let label = UILabel()
            container.addSubview(image)
            container.addSubview(label)
            self.view.addSubview(container)
            
            [image, label, container].forEach {
                $0.translatesAutoresizingMaskIntoConstraints = false
            }
    
            container.backgroundColor = UIColor(red: 0.0, green: 0.5, blue: 1.0, alpha: 1.0)
            //image.image = UIImage(named: "passport")
            image.image = UIImage(named: "swiftRed")
            label.backgroundColor = .yellow
            label.numberOfLines = 0
            label.font = UIFont(name:"Helvetica Neue", size: 15)
            label.text = text
    
            // respect the safe area
            let g = view.safeAreaLayoutGuide
            
            NSLayoutConstraint.activate([
                
                // constrain container Top / Leading / Trailing
                container.topAnchor.constraint(equalTo: view.topAnchor),
                container.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                container.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                
                // we'll use the label height to determine the container height
                
                // image view Top = 8 / Leading = 15
                image.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
                image.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 15.0),
                
                // image view width = 30 / height == width (1:1 ratio)
                image.widthAnchor.constraint(equalToConstant: 30.0),
                image.heightAnchor.constraint(equalTo: image.widthAnchor),
                
                // label Top aligned to Top of image view
                label.topAnchor.constraint(equalTo: image.topAnchor),
                
                // label Leading == image view Trailing + 20
                label.leadingAnchor.constraint(equalTo: image.trailingAnchor, constant: 20.0),
                
                // label Trailing = container Trailing - 15
                label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -15.0),
    
                // container bottom should be
                //  At Least 8-pts below the image view bottom
                // AND
                //  At Least 8-pts below the label bottom
                container.bottomAnchor.constraint(greaterThanOrEqualTo: image.bottomAnchor, constant: 8.0),
                container.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor, constant: 8.0),
    
            ])
            
        }
    
    }
    

    Result, with short text:

    enter image description here

    Result with long text:

    enter image description here

    Note that it auto-adjusts when the size changes - such as with device rotation:

    enter image description here