Adding Static Text with UILabel – RubyMotion

Adding Static Text with UILabel

Animations are fun, but we also need to display information long enough for the user to actually read it. In most cases, we can use UILabel to display static text. Labels can be very flexible, allowing you to change everything from the font to minute adjustments with the text baseline, and are really easy to get up and running. Let’s add one to our little app.

We’re going to add a UILabel to each box, displaying its index in subviews. We probably wouldn’t ship that sort of feature, but it’s really handy for debugging and might give us a better idea of what’s going on in our animation. UILabel is really lightweight, so it won’t be a pain to add.

Adding labels is going to occur in a new method called add_label_to_box. This method will figure out the index of a given box’s UIView instance and add the correct UILabel. This is the important part of the code, so let’s take a look at it first.

 def​ add_label_to_box(box)
  box.subviews.each ​do​ |subview|
  subview.removeFromSuperview
 end
 
  index_of_box = @window.subviews.index(box)
  label = UILabel.alloc.initWithFrame(CGRectZero)
  label.text = ​"​​#{​index_of_box​}​​"
  label.textColor = UIColor.whiteColor
  label.backgroundColor = UIColor.clearColor
  label.sizeToFit
  label.center = [box.frame.size.width / 2, box.frame.size.height / 2]
  box.addSubview(label)
 end

We start by removing all subviews from box, which handles the case where we call this method multiple times on the same view (which we will). Our label is initialized with CGRectZero, which is shorthand for a rectangle at the origin and no size. After we set the text appropriately, we call sizeToFit just like UIButton. The UILabel implementation of sizeToFit will precisely fill the frame to fit the text, leaving no padding. Then we use the center property of UIView, which is shorthand for putting the center of a view at a point (as opposed to the upper-left corner).

Remember how we said subviews are positioned within their parent? Even though we set the label to be centered at a coordinate like (50, 50), it can exist at a different point within the window. As our animation slides the box, its label will move too.

Not too bad, right? The only UILabel-exclusive properties in this example are text and textColor; everything else is inherited from UIView. Now we need to actually call this method.

We usually want to go through all the boxes each time we update the labels so we can be absolutely sure our labels are in sync with subviews. To make our lives easier, we’re going to refactor the logic for picking out boxes from @window.subviews into one method that simply returns only the boxes.

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

We can combine our two new methods into one really great helper method that takes care of everything.

 def​ add_labels_to_boxes
  self.boxes.each ​do​ |box|
  add_label_to_box(box)
 end
 end

Finally, we can put these to some use, first in application:didFinishLaunchingWithOptions:

 @window.addSubview(@blue_view)
»add_labels_to_boxes

and then down in add_tapped.

 @window.insertSubview(new_view, atIndex:0)
»add_labels_to_boxes

Lastly, we need to reset the labels for each box after we run the removal animation. We’re going to change our other_views to use self.boxes instead of its own array construction. Then we’re going to use our handy add_labels_to_boxes to sync all the labels again.

 def​ remove_tapped
» other_views = self.boxes
  last_view = other_views.last
 completion_block = lambda { |finished|
  last_view.removeFromSuperview
» add_labels_to_boxes
 }

Whew. Our UILabel was only a small part of our changes, but now we can clearly see how our view hierarchy behaves at runtime. Run the app, and you should see labels appear as in the following figure.


Figure 4. Labels reset for each box