# Teacup handlers can modify (or alias) styling methods. For instance, the # UIButton class doesn't have a `title` attribute, but we all know that what we # *mean* when we say `title: "button title"` is `setTitle("button title", # forControlState:UIControlStateNormal)`. A UIButton handler accomplishes this # translation. # # You can write your own handlers! # # Teacup.handler UIButton, :title do |view, value| # view.setTitle(value, forControlState:UIControlStateNormal) # end # # You can declare multiple names in the Teacup.handler method to create aliases # for your handler: # # Teacup.handler UIButton, :returnKeyType, :returnkey { |view, keytype| # view.setReturnKeyType(keytype) # } # # Since teacup already supports translating a property like `returnKeyType` into # the setter `setReturnKeyType`, you could just use an alias here instead. # Assign a hash to `Teacup.alias`: # # Teacup.alias UIButton, :returnkey => :returnKeyType module Teacup module_function # Some properties need to be assigned before others (size in particular, so # that `:center_x` can work properly). This hash makes sure the lower # priority styles (default priority is 0) get applied before higher ones. Priorities = { frame: 1, sizeToFit: 2, size: 2, width: 2, height: 2, center: 3, # set the center last; this is the main reason for all this priority nonsense center_x: 3, center_y: 3, } Priorities.default = 0 # applies a Hash of styles, and converts the frame styles (origin, size, top, # left, width, height) into one frame property. # # For UIAppearance support, the class of the UIView class that is being # modified can be passed in def apply_hash(target, properties, klass=nil) properties.sort do |a, b| priority_a = Priorities[a[0]] priority_b = Priorities[b[0]] priority_a <=> priority_b end.each do |key, value| Teacup.apply target, key, value, klass end end # Applies a single style to a target. Delegates to a teacup handler if one is # found. def apply(target, key, value, klass=nil) # note about `debug`: not all objects in this method are a UIView instance, # so don't assume that the object *has* a debug method. if value.is_a? Proc if value.arity == 1 value = value.call(target) else value = target.instance_exec(&value) end end klass ||= target.class handled = false klass.ancestors.each do |ancestor| if Teacup.handlers[ancestor].has_key? key NSLog "#{ancestor.name} is handling #{key} = #{value.inspect}" if target.respond_to? :debug and target.debug if Teacup.handlers[ancestor][key].arity == 1 target.instance_exec(value, &Teacup.handlers[ancestor][key]) else Teacup.handlers[ancestor][key].call(target, value) end handled = true break end end return if handled # you can send methods to subviews (e.g. UIButton#titleLabel) and CALayers # (e.g. UIView#layer) by assigning a hash to a style name. if value.is_a? Hash return Teacup.apply_hash target.send(key), value end if key =~ /^set[A-Z]/ assign = nil setter = key.to_s + ':' else assign = key.to_s + '=' setter = 'set' + key.to_s.sub(/^./) {|c| c.capitalize} + ':' end Teacup.apply_method(target, assign, setter, value) end def handlers @teacup_handlers ||= Hash.new{ |hash,klass| hash[klass] = {} } end def handler klass, *stylenames, &block if stylenames.length == 0 raise TypeError.new "No style names assigned in Teacup[#{klass.inspect}]##handler" else stylenames.each do |stylename| Teacup.handlers[klass][stylename] = block end end self end def alias klass, aliases aliases.each do |style_alias, style_name| Teacup.handlers[klass][style_alias] = proc { |view, value| Teacup.apply view, style_name, value } end self end end