1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
//
// AccountInputGroupView.swift
// MullvadVPN
//
// Created by pronebird on 22/03/2019.
// Copyright © 2019 Amagicom AB. All rights reserved.
//
import UIKit
@IBDesignable class AccountInputGroupView: UIView {
@IBOutlet var textField: UITextField!
private let borderRadius = CGFloat(8)
private let borderWidth = CGFloat(2)
private let borderLayer = CAShapeLayer()
private let backgroundLayer = CAShapeLayer()
private let maskLayer = CALayer()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: - CALayerDelegate
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
guard layer == self.layer else { return }
// extend the border frame outside of the content area
let borderFrame = layer.bounds.insetBy(dx: -borderWidth * 0.5, dy: -borderWidth * 0.5)
// create a bezier path for border
let borderPath = borderBezierPath(size: borderFrame.size)
// update the background layer mask
maskLayer.frame.size = borderFrame.size
maskLayer.contents = backgroundMaskImage(borderPath: borderPath).cgImage
backgroundLayer.frame = borderFrame
borderLayer.path = borderPath.cgPath
borderLayer.frame = borderFrame
}
// MARK: - Notifications
@objc func textDidBeginEditing() {
updateBorderStyle()
}
@objc func textDidEndEditing() {
updateBorderStyle()
}
// MARK: - Private
private func setup() {
backgroundColor = UIColor.clear
borderLayer.lineWidth = borderWidth
borderLayer.strokeColor = UIColor.clear.cgColor
borderLayer.fillColor = UIColor.clear.cgColor
backgroundLayer.backgroundColor = UIColor.white.cgColor
backgroundLayer.mask = maskLayer
layer.insertSublayer(borderLayer, at: 0)
layer.insertSublayer(backgroundLayer, at: 0)
addTextFieldNotificationObservers()
}
private func addTextFieldNotificationObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(textDidBeginEditing), name: UITextField.textDidBeginEditingNotification, object: textField)
NotificationCenter.default.addObserver(self, selector: #selector(textDidEndEditing), name: UITextField.textDidEndEditingNotification, object: textField)
}
private func updateBorderStyle() {
let borderColor = textField.isEditing ? UIColor.accountTextFieldBorderColor : UIColor.clear
borderLayer.strokeColor = borderColor.cgColor
}
private func borderBezierPath(size: CGSize) -> UIBezierPath {
let borderPath = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: borderRadius)
borderPath.lineWidth = borderWidth
return borderPath
}
private func backgroundMaskImage(borderPath: UIBezierPath) -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: borderPath.bounds)
return renderer.image { (ctx) in
borderPath.fill()
// strip out any overlapping pixels between the border and the background
borderPath.stroke(with: .clear, alpha: 0)
}
}
}
|