Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding Bottom Line Border To A TextView - iOS

what is the best way to add a bottom line border to a TextView? i have tried this but it seems it doesn't work. It is affected by the scrolling ability of the textview and the line is being drawn in the middle of the TextView

func addBottomBorderWithColor(color: UIColor, width: CGFloat) {
        let border = CALayer()
        border.backgroundColor = color.CGColor
        border.frame = CGRectMake(0, self.frame.size.height - width, self.frame.size.width, width)
        self.layer.addSublayer(border)
        self.layer.masksToBounds = true
 }

Edit: The TextView has a dynamic height.

like image 911
Ali Alebrahim Avatar asked Jul 12 '16 10:07

Ali Alebrahim


2 Answers

Swift 4 Version

func addBottomBorderWithColor() {
    let border = CALayer()
    border.backgroundColor = UIColor.black.cgColor
    border.frame = CGRect(x: 0, y: yourTextArea.frame.height - 1, width: yourTextArea.frame.width, height: 1)
    yourTextArea.layer.addSublayer(border)
    self.view.layer.masksToBounds = true
}
like image 128
Carioni Avatar answered Sep 28 '22 19:09

Carioni


Update:

I thought a lot about my original solution.

If you are subclassing UITextView to add a bottom line, then the bottom line had better be a subview of the text view itself, rather than its super view's.

And finally, I figure out one solution that can add bottom line as a subview of TextView itself and the bottom line will not move when the user scrolls the text of TextView. In your view controller, you can also change the frame of TextView dynamically, and the bottom line will also stick to the the bottom.

Here is the reference code:

import UIKit

class TextView: UITextView {

    var border: UIView
    var originalBorderFrame: CGRect
    var originalInsetBottom: CGFloat

    deinit {
        removeObserver(self, forKeyPath: "contentOffset")
    }

    override var frame: CGRect {
        didSet {
            border.frame = CGRectMake(0, frame.height+contentOffset.y-border.frame.height, frame.width, border.frame.height)
            originalBorderFrame  = CGRectMake(0, frame.height-border.frame.height, frame.width, border.frame.height);
        }
    }

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if keyPath == "contentOffset" {
            border.frame = CGRectOffset(originalBorderFrame, 0, contentOffset.y)
        }
    }

    func addBottomBorderWithColor(color: UIColor, width: CGFloat) {
        border.backgroundColor = color
        border.frame = CGRectMake(0, frame.height+contentOffset.y-width, self.frame.width, width)
        originalBorderFrame = CGRectMake(0, frame.height-width, self.frame.width, width)
        textContainerInset.bottom = originalInsetBottom+width
    }
}

Note: Since I used to write code in Objective-C, I am not familiar with Swift. The code above is only for your reference (though I have tested the corresponding Objective-C code, and it works as expected):

  • As you can see, there is no initialisation code. I have tried to write such code, but it always shows an error and I still have no idea about that. Just make sure to add the below code to your TextView initialisation code:

    border = UIView()
    addSubview(border)
    originalInsetBottom = textContainerInset.bottom
    addObserver(self, forKeyPath: "contentOffset", options: .New, context: nil)
    
  • I am not familiar with the concept of optional value, wrap, unwrap... So you should add ?, ! to the code if needed.

Original answer:

Does self in your code mean TextView?

If so, when you add border as a sublayer of the TextView, the border will move up and down when the user scrolls the text of TextView.

Try to add border as a sublayer of TextView's super view rather than TextView itself.

Here is the code (Note that I change border from CALayer to UIView):

func addBottomBorderWithColor(color: UIColor, width: CGFloat) {
     let border = UIView()
     border.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y+self.frame.height-width, textView.frame.width, width)
     border.backgroundColor = color
     self.superview!.insertSubview(border, aboveSubview: textView)
 }

Here is the capture:

enter image description here

PS. I suggest you to change the second paramter name from width to height since width is ambiguous in this context.

like image 30
Cokile Ceoi Avatar answered Sep 28 '22 18:09

Cokile Ceoi