blob: afa7c65fc5fbbc87d43c88364222af9047c86687 (
plain)
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
//
// CustomTextView.swift
// MullvadVPN
//
// Created by pronebird on 16/09/2020.
// Copyright © 2020 Mullvad VPN AB. All rights reserved.
//
import Foundation
import UIKit
private let kTextViewCornerRadius = CGFloat(4)
class CustomTextView: UITextView {
var roundCorners: Bool = true {
didSet {
layer.cornerRadius = roundCorners ? kTextViewCornerRadius : 0
}
}
/// Placeholder string
var placeholder: String? {
set {
placeholderTextLabel.text = newValue
}
get {
return placeholderTextLabel.text
}
}
/// Placeholder text label
private let placeholderTextLabel = UILabel()
/// Placeholder label constraints
private var placeholderConstraints = [NSLayoutConstraint]()
override var textContainerInset: UIEdgeInsets {
didSet {
setNeedsUpdateConstraints()
}
}
override var font: UIFont? {
didSet {
placeholderTextLabel.font = self.font ?? UIFont.preferredFont(forTextStyle: .body)
}
}
/// Placeholder text inset derived from `textContainerInset`
private var placeholderTextInset: UIEdgeInsets {
var placeholderInset = textContainerInset
// Offset the placeholder label to match with text view rendering.
placeholderInset.top += 0.5
return placeholderInset
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
placeholderTextLabel.textColor = UIColor.TextField.placeholderTextColor
placeholderTextLabel.highlightedTextColor = UIColor.TextField.placeholderTextColor
placeholderTextLabel.translatesAutoresizingMaskIntoConstraints = false
placeholderTextLabel.numberOfLines = 0
addSubview(placeholderTextLabel)
// Create placeholder constraints
placeholderConstraints = [
placeholderTextLabel.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
placeholderTextLabel.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor),
placeholderTextLabel.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor),
placeholderTextLabel.bottomAnchor.constraint(lessThanOrEqualTo: safeAreaLayoutGuide.bottomAnchor),
]
NSLayoutConstraint.activate(placeholderConstraints)
// Set visual appearance
textColor = UIColor.TextField.textColor
layer.cornerRadius = kTextViewCornerRadius
clipsToBounds = true
// Set content padding
contentInset = .zero
textContainerInset = UIEdgeInsets(top: 12, left: 14, bottom: 12, right: 14)
self.textContainer.lineFragmentPadding = 0
// Handle placeholder visibility
NotificationCenter.default.addObserver(
forName: NSTextStorage.didProcessEditingNotification,
object: textStorage,
queue: OperationQueue.main) { [weak self] (note) in
self?.updatePlaceholderVisibility()
}
updatePlaceholderVisibility()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateConstraints() {
let textInset = placeholderTextInset
for constraint in placeholderConstraints {
switch constraint.firstAttribute {
case .top:
constraint.constant = textInset.top
case .leading:
constraint.constant = textInset.left
case .trailing:
constraint.constant = -textInset.right
case .bottom:
constraint.constant = -textInset.bottom
default:
break
}
}
super.updateConstraints()
}
private func updatePlaceholderVisibility() {
placeholderTextLabel.isHidden = textStorage.length > 0
}
}
|