iosswiftuiview

how to add left green border with curved top and bottom?


Here is the image for reference.

As shown in the image how to add left side green border?

I want to add this type of border to UIview. I have tried to put this type of image inside view but that is not working in my case because it can't manage with dynamic height of the cell.

so please give me a proper solution to add layer to a view.

Edit:

private func addBorderImage() {
    print("commit init")
    guard let img = UIImage(named: "BorderLeft") else {
        fatalError("Could not load image named \"stretch1\"!!!")
    }
    // create a resizable version
    let stretchImg = img.resizableImage(withCapInsets: .init(top: 12.0, left: 0.0, bottom: 12.0, right: 0.0))
    let imgView = UIImageView()
    imgView.image = stretchImg.withRenderingMode(.alwaysTemplate)
    imgView.tintColor = UIColor(named: "AccentColor")
    
    imgView.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(imgView)
    
    let g = contentView.layoutMarginsGuide
    NSLayoutConstraint.activate([
        imgView.topAnchor.constraint(equalTo: self.topAnchor, constant: 8.0),
        imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: -10.0),
        imgView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8.0),
        imgView.widthAnchor.constraint(equalToConstant: 25.0),
    ])
    
}

and I am using png image but resizing is not working.


Solution

  • You can do this with a resizableImage.

    We'll start with this image (clipped from your posted image):

    enter image description here

    It is 11 x 32 (pixels).

    We can make it "stretchable" using func resizableImage(withCapInsets capInsets: UIEdgeInsets) -> UIImage (docs) like this:

    .resizableImage(withCapInsets: .init(top: 12.0, left: 0.0, bottom: 12.0, right: 0.0))
    

    Now the top 12-pixels and bottom 12-pixels will not stretch ... only the middle part will:

    enter image description here

    Sample code:

    class StretchTestVC: UIViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .systemBackground
            
            guard let img = UIImage(named: "stretch1") else {
                fatalError("Could not load image!!!")
            }
            
            // create a resizable version
            let stretchImg = img.resizableImage(withCapInsets: .init(top: 12.0, left: 0.0, bottom: 12.0, right: 0.0))
            
            // now let's add 8 image views with increasing heights
            var x: CGFloat = 40.0
            var h: CGFloat = 60.0
            
            for _ in 1...8 {
                let imgView = UIImageView()
                imgView.image = stretchImg
                imgView.frame = .init(x: x, y: 100.0, width: 11.0, height: h)
                view.addSubview(imgView)
                
                x += 30.0
                h += 50.0
            }
        }
        
    }
    

    Edit - really quick example with variable height table view cells...

    class StretchCellViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
        
        let rowsInCell: [Int] = [
            2, 3, 7, 5, 2, 4, 8, 5, 9, 6,
        ]
        
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .systemBackground
            
            let tableView = UITableView()
            tableView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(tableView)
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                tableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
                tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
                tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
                tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            ])
            
            tableView.register(StretchImageCell.self, forCellReuseIdentifier: StretchImageCell.identifier)
            tableView.dataSource = self
            tableView.delegate = self
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return rowsInCell.count
        }
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let c = tableView.dequeueReusableCell(withIdentifier: StretchImageCell.identifier, for: indexPath) as! StretchImageCell
            c.fillData(numRows: rowsInCell[indexPath.row])
            return c
        }
    }
    
    class StretchImageCell: UITableViewCell {
        
        static let identifier: String = "StretchImageCell"
        
        private let stack = UIStackView()
        
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            commonInit()
        }
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            commonInit()
        }
        private func commonInit() {
            guard let img = UIImage(named: "stretch1") else {
                fatalError("Could not load image named \"stretch1\"!!!")
            }
            // create a resizable version
            let stretchImg = img.resizableImage(withCapInsets: .init(top: 12.0, left: 0.0, bottom: 12.0, right: 0.0))
            let imgView = UIImageView()
            imgView.image = stretchImg
    
            stack.axis = .vertical
            stack.spacing = 8
            
            imgView.translatesAutoresizingMaskIntoConstraints = false
            contentView.addSubview(imgView)
            stack.translatesAutoresizingMaskIntoConstraints = false
            contentView.addSubview(stack)
            
            let g = contentView.layoutMarginsGuide
            NSLayoutConstraint.activate([
                imgView.topAnchor.constraint(equalTo: self.topAnchor, constant: 8.0),
                imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
                imgView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8.0),
                imgView.widthAnchor.constraint(equalToConstant: 11.0),
    
                stack.topAnchor.constraint(equalTo: g.topAnchor),
                stack.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 4.0),
                stack.trailingAnchor.constraint(equalTo: g.trailingAnchor),
                stack.bottomAnchor.constraint(equalTo: g.bottomAnchor),
            ])
            for _ in 1...10 {
                let v = UILabel()
                v.font = .systemFont(ofSize: 24.0, weight: .light)
                v.backgroundColor = .init(white: 0.95, alpha: 1.0)
                stack.addArrangedSubview(v)
            }
        }
    
        public func fillData(numRows: Int) {
            // hide all the labels in the stack view
            for v in stack.arrangedSubviews {
                v.isHidden = true
            }
            // limit to 10 label rows
            let n = min(10, numRows)
            // now set label text and un-hide
            for i in 0..<numRows {
                if let v = stack.arrangedSubviews[i] as? UILabel {
                    v.text = "Label number: \(i+1)"
                    v.isHidden = false
                }
            }
        }
    }
    

    Result:

    enter image description here