1

I've implemented a subclass of UITextField that has padding as follows:

methods taken from here

- (CGRect)textRectForBounds:(CGRect)bounds {
    return CGRectInset(bounds, 10, 10);
}

- (CGRect)editingRectForBounds:(CGRect)bounds {
    return CGRectInset(bounds, 10, 10);
}

With a combination of the above, setting the textField as the firstResponder in viewWillAppear:, and having an initial starting value in the field, I've run into an issue where the text is invisible until the field is edited, and then is fine after that:

enter image description here

When this occurs, I also see the following message in the console:

-[<CALayer: 0x15f000da0> display]: Ignoring bogus layer size (179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000, 20.000000), contentsScale 3.000000, backing store size (inf, 60.000000)

I thought perhaps the bounds value was out of whack, and that was what was causing both the visual bug and error, but when I log the bounds I get a bunch of values that appear to be valid, such as: {{0, 0}, {345, 40}}

Commenting out those two methods above, to basically be a normal UITextField, seems to fix the issue. I've tried adding calls to super in each of those methods, just in case that were to help, to no avail. Any ideas?

Update - Moving the call to becomeFirstResponder to viewDidAppear seems to also fix the issue, but that pretty much kills the flow in what is basically a wizard, and thus the previous screen also has the keyboard on screen, so I'd like to avoid the keyboard dismissing and reappearing on every screen.

Community
  • 1
  • 1
Mike
  • 9,765
  • 5
  • 34
  • 59
  • "setting the textField as the firstResponder in viewWillAppear:" How does that makes sense? In `viewWillAppear`, the text field is not in the view hierarchy yet. That is what "will" means; appearing is still _in the future_. How _can_ it be first responder? – matt Oct 14 '15 at 14:30
  • "so I'd like to avoid the keyboard dismissing and reappearing on every screen" Understood, but there's nothing you can do about that. Look at any Apple app. When you change screens, the keyboard is dismissed. You can't magically get a smooth flow here. – matt Oct 14 '15 at 14:32
  • @matt Considering it works in every other instance I've ever tried it, and I haven't seen it written anywhere in the docs otherwise, although I may have missed it, I suppose I assumed it was *ok* to do. I absolutely don't discount the idea I may have been doing it wrong all these years, but this is the first I've heard of it. What do you mean *specifically* by "is not in the view hierachy yet"? The textField exists as a subview of a view controller that is in the navigation stack, what am I missing? – Mike Oct 14 '15 at 14:39
  • The view and the view controller are not the same thing. The view controller is in the view controller hierarchy, but the view is not yet in the view hierarchy; the word "will" means that this view has not yet gotten into the interface. – matt Oct 14 '15 at 14:49
  • Yeah, you're absolutely right. I just checked the docs for UIResponder: "However, you should only call it on that view if it is part of a view hierarchy. If the view’s window property holds a UIWindow object, it has been installed in a view hierarchy; if it returns nil, the view is detached from any hierarchy." In viewWillAppear the field's superview's window is nil, which is definitely the problem. – Mike Oct 14 '15 at 14:57
  • I still find it interesting that this problem would only manifest when using this custom textField, and calling `becomeFirstResponder` on any textfield/view in view will appear works (and has since at least iOS6) *and provides a much smoother UX*. – Mike Oct 14 '15 at 14:59
  • 1
    I find it interesting too, but - call me a sissy if you like - I prefer to do what the docs tell me to do. Cocoa is a massive invisible framework; I don't like to risk playing around with edge cases that it doesn't support, especially, when the docs _tell_ me it doesn't support something. It may be lucky that calling `becomeFirstResponder` in `viewWillAppear` has worked for you, but the docs tell you that what you were doing was always wrong, and now, when, you have hit a situation where it has come back to bite you, you can hardly cry foul. – matt Oct 14 '15 at 15:13
  • I suppose you'd also like some sort of positive suggestion... Could you rethink your interface? What if you could get from text field A to text field B _without_ pushing a new view controller onto the navigation controller? – matt Oct 14 '15 at 15:16
  • Agreed - not trying to cry foul, I just wish they made it possible to do this without interrupting the flow with keyboard animations. I'll switch to `viewDidAppear` and just live with it. This implementation is a rather complicated wizard that involves multiple pages each with different related sets of fields, so that'll have to be the way I do it, I reckon. – Mike Oct 14 '15 at 15:16
  • 1
    I'm still thinking about alternatives. Suppose all this were happening in a milieu where something _persists_ as we move from screen to screen — something that can be first responder and that summons the keyboard. Then before we move, we transfer f.r. status to it, and after we move, we transfer f.r. status to the next text field. The keyboard thus persists through the change. – matt Oct 14 '15 at 15:27
  • 2
    For example, let's say this is a normal navigation interface. Then we have a nav bar. Then we can have a text field in the nav bar. Then that text field can be first responder. Could we use that to "hold" the keyboard open for us as we push? If the text field is tiny (and perhaps usually invisible) the user will never know. I've never tried this but it could be worth a go. – matt Oct 14 '15 at 15:29
  • @matt excellent idea, I'll try out a few things and see what I come up with. Thank you for your help - you likely just sold a book from this. :) – Mike Oct 14 '15 at 15:30

1 Answers1

1

Thanks to @matt, I discovered that the issue is caused because I'm calling becomeFirstResponder on the textfield in viewWillAppear. Although this has worked for me with normal textfields and textviews for years without issue, it appears this is finally an instance where an issue arose.

According to the UIResponder docs for becomeFirstResponder:

You may call this method to make a responder object such as a view the first responder. However, you should only call it on that view if it is part of a view hierarchy. If the view’s window property holds a UIWindow object, it has been installed in a view hierarchy; if it returns nil, the view is detached from any hierarchy.

This was validated by printing [[self.textfield superview] window] in the debugger, and in viewWillAppear the window is nil, whereas it isn't in viewDidAppear. Learn something new every day.

Mike
  • 9,765
  • 5
  • 34
  • 59