Skip to content

Instantly share code, notes, and snippets.

@scizo
Created June 30, 2012 17:27
Show Gist options
  • Save scizo/3024727 to your computer and use it in GitHub Desktop.
Save scizo/3024727 to your computer and use it in GitHub Desktop.

Revisions

  1. scizo renamed this gist Jun 30, 2012. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. scizo created this gist Jun 30, 2012.
    130 changes: 130 additions & 0 deletions gistfile1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,130 @@
    class AppDelegate
    def application(application, didFinishLaunchingWithOptions:launchOptions)
    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
    @window.rootViewController = ExampleViewController.alloc.init

    @window.makeKeyAndVisible
    true
    end
    end

    class ExampleViewController < UIViewController
    def init
    return self if self.initWithNibName(nil, bundle: nil)
    end

    def loadView
    self.view = ExampleView.alloc.init
    end
    end

    class View < UIView
    INITIALIZERS = {
    UIView => 'initWithFrame:',
    UIImageView => 'initWithImage:',
    UISegmentedControl => 'initWithItems:',
    }

    class << self
    def current_views
    @current_views ||= []
    end

    def attributes
    @attributes ||= []
    end

    def view_builders
    @view_builders ||= []
    end

    def subview(klass, *args, &attributes_builder)
    options = args.pop if args.last.is_a?(Hash)
    options = {} unless options

    if current_views.empty?
    view_builders << [klass, args, attributes_builder, options]
    return
    end

    build(klass, args, attributes_builder, options)
    end

    def method_missing(name, *args)
    method = "set#{camelize(name)}:"

    if current_views.empty?
    attributes.push [method, args.first]
    return
    end

    view = current_views.last
    super unless view.respond_to?(method)

    puts "::: Performing #{method} with #{args.first.inspect} on #{view.inspect}"
    # I would rather use send, but it crashes
    view.performSelectorOnMainThread(method, withObject: args.first, waitUntilDone: true)
    end

    private

    def build(klass, args, attributes_builder, options)
    args = args.map{ |arg| arg.respond_to?(:call) ? arg.call : arg }
    view = klass.alloc.send(INITIALIZERS[klass], *args)

    current_views << view
    attributes_builder.call if attributes_builder
    current_views.pop

    root_view, last_view = current_views.first, current_views.last
    root_view.views[options[:name]] = view if options[:name]
    last_view.addSubview view
    view
    end

    def camelize(term)
    string = term.to_s
    string = string.sub(/^[a-z\d]*/) { $&.capitalize }
    #string = string.sub(/^(?:(?=a)b(?=\b|[A-Z_])|\w)/) { $&.downcase }
    string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }
    end
    end

    attr_reader :views

    def init
    if self.initWithFrame(CGRectZero)
    @views = {}

    self.class.attributes.each do |(method, arg)|
    self.performSelectorOnMainThread(method, withObject: arg, waitUntilDone: true)
    end

    self.class.current_views << self
    self.class.view_builders.each do |builder|
    view = self.class.build(*builder)
    end
    self.class.current_views.pop
    end

    self
    end
    end

    class ExampleView < View
    frame [[0, 0], [320, 460]]
    background_color UIColor.redColor

    subview UIView, [[10, 10], [300, 440]] do
    background_color UIColor.greenColor

    subview UIView, [[10, 10], [280, 420]] do
    background_color UIColor.blueColor

    subview UISegmentedControl, ["Example", "Test"] do
    frame [[40, 20], [200, 40]]
    tint_color UIColor.blackColor
    end
    end
    end
    end