Skip to content

Commit 28b22b4

Browse files
committed
Refactoring and fixing a bug around nib-instantiated views.
1 parent cb66dc3 commit 28b22b4

File tree

1 file changed

+41
-31
lines changed

1 file changed

+41
-31
lines changed

MPTextView.m

+41-31
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@
77

88
#import "MPTextView.h"
99

10-
// Manually-selected label offset to align placeholder label with real text.
10+
// Manually-selected label offsets to align placeholder label with text entry.
1111
static CGFloat const kLabelLeftOffset = 8.f;
12+
static CGFloat const kLabelTopOffset = 0.f;
13+
14+
// When instantiated from IB, the text view has an 8 point top offset:
15+
static CGFloat const kLabelTopOffsetFromIB = 8.f;
16+
// On retina iPhones and iPads, the label is offset by 0.5 points:
1217
static CGFloat const kLabelTopOffsetRetina = 0.5f;
1318

1419
@interface MPTextView ()
1520

1621
@property (nonatomic, strong) UILabel *placeholderLabel;
1722

18-
// Calculate and save the label's left and top offset.
19-
@property (nonatomic, assign) CGSize labelOffset;
23+
// The top offset differs when the view is instantiated from IB or programmatically.
24+
// Use this to track the instantiation route and offset the label accordingly.
25+
@property (nonatomic, assign) CGFloat topLabelOffset;
2026

2127
// Handle text changed event so we can update the placeholder appropriately
2228
- (void)textChanged:(NSNotification *)note;
@@ -31,23 +37,26 @@ @implementation MPTextView
3137
- (id)initWithCoder:(NSCoder *)aDecoder {
3238
self = [super initWithCoder:aDecoder];
3339
if (self) {
40+
// Account for IB offset:
41+
_topLabelOffset = kLabelTopOffsetFromIB;
3442
[self finishInitialization];
3543
}
3644
return self;
3745
}
3846

39-
4047
- (id)initWithFrame:(CGRect)frame {
4148
self = [super initWithFrame:frame];
4249
if (self) {
50+
_topLabelOffset = kLabelTopOffset;
4351
[self finishInitialization];
4452
}
4553
return self;
4654
}
4755

48-
4956
// Private method for finishing initialization.
50-
// Rather than muck with designated initializers, let's do it the easy way.
57+
// Since this class isn't documented for subclassing,
58+
// I don't feel comfortable changing the initializer chain.
59+
// Let's do it this way rather than overriding UIView's designated initializer.
5160
- (void)finishInitialization {
5261
// Sign up for notifications for text changes:
5362
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -56,44 +65,48 @@ - (void)finishInitialization {
5665
object:self];
5766

5867
CGFloat labelLeftOffset = kLabelLeftOffset;
59-
CGFloat labelTopOffset = 0.f;
68+
// Use our calculated label offset from initWith…:
69+
CGFloat labelTopOffset = self.topLabelOffset;
6070

6171
// On retina iPhones and iPads, the label is offset by 0.5 points.
6272
if ([[UIScreen mainScreen] scale] == 2.0) {
63-
labelTopOffset = kLabelTopOffsetRetina;
73+
labelTopOffset += kLabelTopOffsetRetina;
6474
}
65-
self.labelOffset = CGSizeMake(labelLeftOffset, labelTopOffset);
6675

67-
[self createPlaceholderLabel];
76+
CGSize labelOffset = CGSizeMake(labelLeftOffset, labelTopOffset);
77+
CGRect labelFrame = [self placeholderLabelFrameWithOffset:labelOffset];
78+
[self createPlaceholderLabel:labelFrame];
6879
}
6980

7081

71-
#pragma mark - Placeholder label helper
82+
#pragma mark - Placeholder label helpers
7283

7384
// Create our label:
74-
- (void)createPlaceholderLabel {
75-
CGRect labelFrame = [self calculatePlaceholderLabelFrame];
76-
85+
- (void)createPlaceholderLabel:(CGRect)labelFrame {
7786
self.placeholderLabel = [[UILabel alloc] initWithFrame:labelFrame];
7887
self.placeholderLabel.lineBreakMode = NSLineBreakByWordWrapping;
7988
self.placeholderLabel.numberOfLines = 0;
8089
self.placeholderLabel.font = self.font;
8190
self.placeholderLabel.backgroundColor = [UIColor clearColor];
8291
self.placeholderLabel.text = self.placeholderText;
83-
// Manually matched to UITextField's placeholder text color.
92+
// Color-matched to UITextField's placeholder text color:
8493
self.placeholderLabel.textColor = [UIColor colorWithWhite:0.71f alpha:1.0f];
8594

95+
// UIKit effects on the UITextView, like selection ranges
96+
// and the cursor, are done in a view above the text view,
97+
// so no need to order this below anything else.
98+
// Add the label as a subview.
8699
[self addSubview:self.placeholderLabel];
87100
}
88101

89-
90-
- (CGRect)calculatePlaceholderLabelFrame {
91-
return CGRectMake(self.labelOffset.width,
92-
self.labelOffset.height,
93-
self.bounds.size.width - (2 * self.labelOffset.width),
94-
self.bounds.size.height - (2 * self.labelOffset.height));
102+
- (CGRect)placeholderLabelFrameWithOffset:(CGSize)labelOffset {
103+
return CGRectMake(labelOffset.width,
104+
labelOffset.height,
105+
self.bounds.size.width - (2 * labelOffset.width),
106+
self.bounds.size.height - (2 * labelOffset.height));
95107
}
96108

109+
97110
#pragma mark - Custom accessors
98111

99112
- (void)setPlaceholderText:(NSString *)string {
@@ -105,16 +118,15 @@ - (void)setPlaceholderText:(NSString *)string {
105118

106119
#pragma mark - UITextView subclass methods
107120

108-
// Keep the placeholder label font in sync with the text view's
121+
// Keep the placeholder label font in sync with the view's text font.
109122
- (void)setFont:(UIFont *)font {
110123
// Call super.
111124
[super setFont:font];
112125

113126
self.placeholderLabel.font = self.font;
114127
}
115128

116-
117-
// Keep placeholder label alignment in sync
129+
// Keep placeholder label alignment in sync with the view's text alignment.
118130
- (void)setTextAlignment:(NSTextAlignment)textAlignment {
119131
// Call super.
120132
[super setTextAlignment:textAlignment];
@@ -135,13 +147,14 @@ - (id)insertDictationResultPlaceholder {
135147
// Call super.
136148
id placeholder = [super insertDictationResultPlaceholder];
137149

138-
// Use .hidden (instead of alpha), since these events also trigger
139-
// -[textChanged] (which has a different criteria by which it shows the label)
150+
// Use -[setHidden] here instead of setAlpha:
151+
// these events also trigger -[textChanged],
152+
// which has a different criteria by which it shows the label,
153+
// but we undeniably know we want this placeholder hidden.
140154
self.placeholderLabel.hidden = YES;
141155
return placeholder;
142156
}
143157

144-
145158
// Update visibility when dictation ends.
146159
- (void)removeDictationResultPlaceholder:(id)placeholder willInsertResult:(BOOL)willInsertResult {
147160
// Call super.
@@ -165,8 +178,7 @@ - (void)updatePlaceholderLabelVisibility {
165178
}
166179
}
167180

168-
169-
// When text is set or changed, update the label's visibility:
181+
// When text is set or changed, update the label's visibility.
170182

171183
- (void)setText:(NSString *)text {
172184
// Call super.
@@ -175,10 +187,8 @@ - (void)setText:(NSString *)text {
175187
[self updatePlaceholderLabelVisibility];
176188
}
177189

178-
179190
- (void)textChanged:(NSNotification *)notification {
180191
[self updatePlaceholderLabelVisibility];
181192
}
182193

183-
184194
@end

0 commit comments

Comments
 (0)