Making Text Dynamic with UITextField – RubyMotion

Making Text Dynamic with UITextField

Most apps have more than just buttons and labels; usually we need the user to enter some data, like a tweet or email address. UITextField is the basic view we use to grab string-type input, and now we’re going to add one to our little box app. It will let the user pick the UIColor of our boxes using simple commands like “red” and “blue.” Let’s get started.

First we need to add the field to the view hierarchy. UITextField puts many configuration options at our disposal, from fonts to the look of the Return key. We won’t be using all of its properties today, but you can consult Apple’s documentation on the class for more information.[9] Our field should be added in app_delegaterb like so:

 @remove_button.addTarget(
  self, action​:"remove_tapped"​,
  forControlEvents​:UIControlEventTouchUpInside​)
 
 @color_field = UITextField.alloc.initWithFrame(CGRectZero)
 @color_field.borderStyle = UITextBorderStyleRoundedRect
 @color_field.text = ​"Blue"
 @color_field.enablesReturnKeyAutomatically = ​true
 @color_field.returnKeyType = UIReturnKeyDone
 @color_field.autocapitalizationType = UITextAutocapitalizationTypeNone
 @color_field.sizeToFit
 @color_field.frame = CGRect.new(
  [@blue_view.frame.origin.x + @blue_view.frame.size.width + 10,
  @blue_view.frame.origin.y + @color_field.frame.size.height],
  @color_field.frame.size)
 @window.addSubview(@color_field)
 
»@color_field.delegate = self

Like every other view in the window, we spend some time setting up the frame and positioning it exactly where we want it. returnKeyType and similar properties control exactly what they say; the only cryptic property we use is UITextBorderStyleRoundedRect, which adds a nice border and inner shadow to our field. By default, UITextFields have empty backgrounds and no default styling.

The most important part of our addition is setting @color_field’s delegate. Much like our application uses AppDelegate as its delegate, other objects use the delegation pattern as a way of sending callback events. The UITextFieldDelegate specification lists all of the methods the delegate object can implement.[10] We aren’t required to implement any of them, but we will implement textFieldShouldReturn: to intercept when the Return/Done key is pressed.

 def​ textFieldShouldReturn(textField)
  color_tapped
  textField.resignFirstResponder
 false
 end

resignFirstResponder looks a little cryptic, but in reality it simply hides the keyboard. In iOS, there’s a concept of a responder chain that determines how events such as taps are propagated among our objects. We won’t deal with the responder chain in this book, but the important thing to remember is that the first responder of a text field is almost always the virtual keyboard. You also may notice that we explicitly return false from textFieldShouldReturn:, but why is that? Whatever you return from this method decides whether the UITextField carries out the default behavior of its Return key; in our case, we’re hiding the keyboard, and the normal action should be avoided.

Finally, we need to implement the color_tapped method we called in textFieldShouldReturn:. We’re going to read the text property of the text field and use the Ruby metaprogramming send method to create a UIColor from that string.

 def​ color_tapped
  color_prefix = @color_field.text
  color_method = ​"​​#{​color_prefix.downcase​}​​Color"
 if​ UIColor.respond_to?(color_method)
  @box_color = UIColor.send(color_method)
  self.boxes.each ​do​ |box|
  box.backgroundColor = @box_color
 end
 else
  UIAlertView.alloc.initWithTitle(​"Invalid Color"​,
 message: ​​"​​#{​color_prefix​}​​ is not a valid color"​,
 delegate: ​​nil​,
 cancelButtonTitle: ​​"OK"​,
 otherButtonTitles: ​​nil​).show
 end
 end

Since we’re feeling friendly, we alert the user if there is no such UIColor for their input. But if we do succeed in creating a color object, we assign it to a @box_color instance variable. We need to go back to other parts of the code to make sure they also use @box_color; that way, events like adding a new box work as expected.

 @window.makeKeyAndVisible
»@box_color = UIColor.blueColor
 @blue_view = UIView.alloc.initWithFrame(CGRect.new([10, 40], [100, 100]))
»@blue_view.backgroundColor = @box_color
 @window.addSubview(@blue_view)
 def​ add_tapped
  new_view = UIView.alloc.initWithFrame(CGRect.new([0, 0], [100, 100]))
» new_view.backgroundColor = @box_color

All we’re doing here is changing the hard-coded use of blueColor to our new instance variable. And there’s one more thing: we need to fix our boxes method to ignore the new UITextField.

 def​ boxes
  @window.subviews.reject ​do​ |view|
» view.is_a?(UIButton) or view.is_a?(UILabel) or view.is_a?(UITextField)
 end
 end

Fantastic; let’s run rake again and play with the text field (see Figure 5, Playing with the text field). Be sure to try more exotic colors like “cyan” and “magenta,” too.


Figure 5. Playing with the text field