Skip to content

Instantly share code, notes, and snippets.

@unnamedd
Last active February 21, 2018 17:51
Show Gist options
  • Save unnamedd/40d29659584fdd0fb86f45f81cbf8900 to your computer and use it in GitHub Desktop.
Save unnamedd/40d29659584fdd0fb86f45f81cbf8900 to your computer and use it in GitHub Desktop.
Extension UIView to add borders in partnership with @diegoventura and @brunogb
import UIKit
import PlaygroundSupport
extension UIView {
private class UIViewNamed: UIView {
var name: String
var thickness: CGFloat? = nil
required init(frame: CGRect = CGRect.zero, name: String) {
self.name = name
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
public enum RectEdge: String {
case top
case right
case bottom
case left
}
open func set(to edge: RectEdge, with color: UIColor, thickness: CGFloat? = nil) {
let view = self.border(to: edge) ?? UIViewNamed(name: edge.rawValue)
let thicknessToApply = thickness ?? view.thickness ?? 1.0
view.backgroundColor = color
view.translatesAutoresizingMaskIntoConstraints = false
view.thickness = thicknessToApply
self.removeConstraints(view.constraints)
self.addSubview(view)
self.constraint(to: view, edge: edge, thickness: thicknessToApply)
}
private func border(to edge: RectEdge) -> UIViewNamed? {
let index = self.subviews.index(where: {
if $0 is UIViewNamed, let view = $0 as? UIViewNamed, view.name == edge.rawValue {
return true
}
return false
})
guard let i = index else {
return nil
}
return self.subviews[i] as? UIViewNamed
}
private func constraint(to view: UIView, edge: RectEdge, thickness: CGFloat) {
switch edge {
case .top:
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(0)-[top(==thickness)]",
options: [],
metrics: ["thickness": thickness],
views: ["top": view]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-(0)-[top]-(0)-|",
options: [],
metrics: nil,
views: ["top": view]))
break
case .right:
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[right(==thickness)]-(0)-|",
options: [],
metrics: ["thickness": thickness],
views: ["right": view]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(0)-[right]-(0)-|",
options: [],
metrics: nil,
views: ["right": view]))
break
case .bottom:
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[bottom(==thickness)]-(0)-|",
options: [],
metrics: ["thickness": thickness],
views: ["bottom": view]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-(0)-[bottom]-(0)-|",
options: [],
metrics: nil,
views: ["bottom": view]))
break
case .left:
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-(0)-[left(==thickness)]",
options: [],
metrics: ["thickness": thickness],
views: ["left": view]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(0)-[left]-(0)-|",
options: [],
metrics: nil,
views: ["left": view]))
break
}
}
}
class ViewController : UIViewController {
var sub: UIView?
func more() {
var frame = sub?.frame
frame?.size.height += 25
sub?.frame = frame!
}
func less() {
var frame = sub?.frame
frame?.size.height -= 25
sub?.frame = frame!
}
override func loadView() {
let view = UIView()
let more = UIButton(frame: CGRect(x: 135, y: 120, width: 40, height: 20))
more.setTitle("+", for: .normal)
more.addTarget(self, action: #selector(self.more), for: .touchUpInside)
more.backgroundColor = .gray
view.addSubview(more)
let less = UIButton(frame: CGRect(x: more.frame.maxX + 10, y: more.frame.minY, width: 40, height: 20))
less.setTitle("-", for: .normal)
less.addTarget(self, action: #selector(self.less), for: .touchUpInside)
less.backgroundColor = .lightGray
view.addSubview(less)
sub = UIView(frame: CGRect(x: more.frame.minX, y: more.frame.maxY + 10, width: 90, height: 50))
sub?.backgroundColor = .green
sub?.set(to: .bottom, with: UIColor.gray, thickness: 5)
view.addSubview(sub!)
view.backgroundColor = UIColor.white
self.view = view
}
}
PlaygroundPage.current.liveView = ViewController()
@brunogb
Copy link

brunogb commented Mar 9, 2017

We can change the UIViewNamed, and add a thickness attribute:

private class UIViewNamed: UIView {
        var name: String
        var thickness: CGFloat? = nil
        required init(frame: CGRect = CGRect.zero, name: String) {
            self.name = name
            
            super.init(frame: frame)
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }

and then on method set(border: we can change it to:

open func set(border edge: RectEdge, with color: UIColor, thickness: CGFloat? = nil) {
        let view = self.border(to: edge) ?? UIViewNamed(name: edge.rawValue)
        let thicknessToApply = thickness ?? view.thickness ?? 1.0
        view.backgroundColor = color
        view.translatesAutoresizingMaskIntoConstraints = false
        view.thickness = thicknessToApply
        self.removeConstraints(view.constraints)
        self.addSubview(view)
        self.constraint(to: view, edge: edge, thickness: thicknessToApply)
    }

what do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment