Skip to content
colinta edited this page Sep 29, 2014 · 8 revisions

UIView

# add a subview
view << subview  # => view.addSubview(subview)
view.unshift(subview)  # adds subview to the bottom of the views

UIView.first_responder  # => returns the first responder, starting at UIApplication.sharedApplication.keyWindow
my_view.first_responder  # => also returns the first responder, but starts looking in my_view
my_view.controller  # => returns the UIViewController that this view belongs to

# convert to UIImage.  retina-ready.
my_view.uiimage
# that will use the `bounds` property to size the image.  but if you want a
# screen shot of the contents of a scroll view, pass in `true` or `:all` to this
# method.
my_scroll_view.uiimage(:all)

# convert a view's frame to another view's coordinates.  This method uses the
# frame: [[0, 0], subview.frame.size] because view.bounds is not always entirely
# reliable for what we're trying to do here.
subview.convert_frame_to(view)
view.convert_frame_from(subview)

# or just the origin
subview.convert_origin_to(view)
view.convert_origin_from(subview)

When defining a UIView subclass, you often have attributes that affect your drawRect method (99% of the time, ALL the attributes affect drawing, right?). So SugarCube adds an attr_updates method, which creates an attribute identical to attr_accessor but the setter calls setNeedsDisplay if the new value != the old value.

class NiftyView < UIView
  attr_updates :insets, :pattern

  def drawRect(rect)
    # ...
  end
end

nifty_view.pattern = 'my_pattern'  # => setNeedsDisplay gets called
nifty_view.pattern = 'my_pattern'  # => setNeedsDisplay doesn't get called, because the value didn't change.

As a convenience, the FrameAccessor methods (https://github.com/AlexDenisov/FrameAccessor) are mixed into UIView. However, bottom and right are not added, because adding top and left would lead to basically reproducing all of geomotion.

view.frame = [[10, 20], [100, 200]]
view.x  # => 10
view.x = 50
view.y = 30
view.y  # => 30
view.width  # => 100
view.height  # => 200

UIButton

Come on, Apple, this is just ridiculous:

button.setTitle('foo', forState: UIControlStateNormal)
# how about...
button.title = 'foo'

You're welcome! 😉 -colinta

UIViewController

You can push (aka the << operator) and pop view controllers onto the childViewControllers array, these are overridden in classes that can define a more useful way of adding controllers, for instance the UINavigationController animates the pushViewController method.

UINavigationController

push, << and pop instead of pushViewController and popViewController.

animated is true for all these.

pop accepts an argument: either a view controller to pop to, or the symbol :root which does what you might expect:

nav_ctlr << root_ctlr
# or nav_ctlr.push(root_ctlr)
nav_ctlr << new_ctlr
# ... imagine we push on a ton of controllers ...
nav_ctlr << another_ctlr
nav_ctlr << last_ctlr
nav_ctlr.pop # => pops to another_ctlr, because it's next on the stack
nav_ctlr.pop parent_ctlr # => pops to parent_ctlr
nav_ctlr.pop :root  # => pops to root_ctlr, because it's on the bottom

UITabBarController

I have mixed feelings about adding this extension, but I needed it, so maybe you will, too... Usually a UITabBarController has a static number of tabs, but in my case, I needed to be able to add one later, when a certain condition was met.

controllers = tabbar_ctlr.viewControllers
controllers << new_ctlr
tabbar_ctlr.setViewControllers(controllers, animated: true)

# =>

tabbar_ctlr << new_ctlr

UILabel

Added simple fit_to_size function to the label, which will start at the supplied font size and then squeeze down until all the text fits. This way you can assure any dynamic text will completely display in a given label frame.

The font size changes instead of the frame size.

# this will try to make the containing text fit at font size 40, but squeeze as needed.
@label.fit_to_size(40)
puts @label.font.pointSize # => Will be 40 or less depending on the font type and label frame.

CALayer

layer << sublayer  # just like UIView's << method

UIPickerView

picker_view[0]  # => picker_view.selectedRowInComponent(0)
picker_view[0] = 1  # => picker_view.selectRow(1, inComponent: 0, animated: true)

UIWebView

web_view.eval_js('alert("alerts are annoying");')
# => web_view.stringByEvaluatingJavaScriptFromString(...)

NSString

# UIImage from name
"my_image".uiimage  # => UIImage.imageNamed("my_image")

# UIFont from name
"my_font".uifont # => UIFont.fontWithName("my_font", size:UIFont.systemFontSize)
"my_font".uifont(20) # => UIFont.fontWithName("my_font", size:20)

# UILabel from string, can accept a font
lbl = "this is important".uilabel
lbl = "this is important".uilabel(UIFont.systemFontOfSize(20))

NSAttributedString

string = NSAttributedString.alloc.initWithString('HEY!', attributes:{NSFontAttributeName => UIFont.boldSystemFontOfSize(20)})
lbl = string.uilabel

Symbol

:bold.uifont  # UIFont.boldSystemFontOfSize(UIFont.systemFontSize)
:bold.uifont(10)  # UIFont.boldSystemFontOfSize(10)
:small.uifontsize # => UIFont.smallSystemFontSize
:small.uifont  # => UIFont.systemFontOfSize(:small.uifontsize)
:bold.uifont(:small)  # UIFont.boldSystemFontOfSize(:small.uifontsize)