Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Sync gh-pages

 # On branch master
# Your branch is ahead of origin/master by 2 commits.
#
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   .DS_Store
#	deleted:    10-api-driven-example/Colr/.gitignore
#	deleted:    10-api-driven-example/Colr/Rakefile
#	deleted:    10-api-driven-example/Colr/app/app_delegate.rb
#	deleted:    10-api-driven-example/Colr/app/controllers/color_controller.rb
#	deleted:    10-api-driven-example/Colr/app/controllers/search_controller.rb
#	deleted:    10-api-driven-example/Colr/app/models/color.rb
#	deleted:    10-api-driven-example/Colr/app/models/tag.rb
#	deleted:    10-api-driven-example/Colr/spec/main_spec.rb
#	deleted:    2-views/Views/.gitignore
#	deleted:    2-views/Views/Rakefile
#	deleted:    2-views/Views/app/app_delegate.rb
#	deleted:    2-views/Views/spec/main_spec.rb
#	deleted:    3-controllers/Controllers/.gitignore
#	deleted:    3-controllers/Controllers/Rakefile
#	deleted:    3-controllers/Controllers/app/app_delegate.rb
#	deleted:    3-controllers/Controllers/app/controllers/TapController.rb
#	deleted:    3-controllers/Controllers/spec/main_spec.rb
#	deleted:    4-containers/.DS_Store
#	deleted:    4-containers/Containers/.gitignore
#	deleted:    4-containers/Containers/Rakefile
#	deleted:    4-containers/Containers/app/app_delegate.rb
#	deleted:    4-containers/Containers/app/controllers/TapController.rb
#	deleted:    4-containers/Containers/spec/main_spec.rb
#	deleted:    4-containers/images/.DS_Store
#	deleted:    5-tables/.DS_Store
#	deleted:    5-tables/Tables/.gitignore
#	deleted:    5-tables/Tables/Rakefile
#	deleted:    5-tables/Tables/app/app_delegate.rb
#	deleted:    5-tables/Tables/app/controllers/AlphabetController.rb
#	deleted:    5-tables/Tables/app/controllers/TapController.rb
#	deleted:    5-tables/Tables/spec/main_spec.rb
#	deleted:    6-animations/Animations/.gitignore
#	deleted:    6-animations/Animations/Rakefile
#	deleted:    6-animations/Animations/app/app_delegate.rb
#	deleted:    6-animations/Animations/spec/main_spec.rb
#	deleted:    7-models/KeyValueFun/.gitignore
#	deleted:    7-models/KeyValueFun/Rakefile
#	deleted:    7-models/KeyValueFun/app/app_delegate.rb
#	deleted:    7-models/KeyValueFun/app/user.rb
#	deleted:    7-models/KeyValueFun/spec/main_spec.rb
#	deleted:    8-testing/Tests/.gitignore
#	deleted:    8-testing/Tests/Rakefile
#	deleted:    8-testing/Tests/app/ButtonController.rb
#	deleted:    8-testing/Tests/app/app_delegate.rb
#	deleted:    8-testing/Tests/spec/main_spec.rb
#	deleted:    Gemfile
#	deleted:    Gemfile.lock
#	deleted:    README.md
#	deleted:    _config.yml
#	deleted:    _layouts/default.html
#	deleted:    _layouts/splash.html
#	deleted:    _plugins/redcarpet2_markdown.rb
#	deleted:    _plugins/sitemap_generator.rb
#	deleted:    _posts/chapters/1900-01-01-0-in-motion.md
#	deleted:    _posts/chapters/1900-01-01-1-hello-motion.md
#	deleted:    _posts/chapters/1900-01-02-2-views.md
#	deleted:    _posts/chapters/1900-01-03-3-controllers.md
#	deleted:    _posts/chapters/1900-01-04-4-containers.md
#	deleted:    _posts/chapters/1900-01-05-5-tables.md
#	deleted:    _posts/chapters/1900-01-06-6-animations.md
#	deleted:    _posts/chapters/1900-01-07-7-models.md
#	deleted:    _posts/chapters/1900-01-08-8-testing.md
#	deleted:    _posts/chapters/1900-01-09-9-http.md
#	deleted:    _posts/chapters/1900-01-10-10-api-driven-example.md
#	modified:   index.html
#	deleted:    more-tables/MoreTables/.gitignore
#	deleted:    more-tables/MoreTables/Rakefile
#	deleted:    more-tables/MoreTables/app/MoreController.rb
#	deleted:    more-tables/MoreTables/app/app_delegate.rb
#	deleted:    more-tables/MoreTables/spec/main_spec.rb
#	deleted:    more-tables/images/table_sections.png
#	deleted:    more-tables/more-tables.md
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	0-in-motion/
#	1-hello-motion/index.html
#	10-api-driven-example/index.html
#	2-views/index.html
#	3-controllers/index.html
#	4-containers/index.html
#	5-tables/index.html
#	6-animations/index.html
#	7-models/index.html
#	8-testing/index.html
#	9-http/
#	sitemap.xml
no changes added to commit (use "git add" and/or "git commit -a")
  • Loading branch information...
commit f73691c4852ac174f1ac51db5165ff1410315fcf 1 parent 70cca16
@clayallsopp authored
View
BIN  .DS_Store
Binary file not shown
View
150 0-in-motion/index.html
@@ -0,0 +1,150 @@
+
+<!doctype html>
+ <head>
+ <title> In Motion | RubyMotion Tutorial</title>
+ <meta name="description" content="" />
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="all" />
+ <meta name="MSSmartTagsPreventParsing" content="true" />
+ <meta name="author" content="Clay Allsopp" />
+
+ <meta property="og:title" content="RubyMotion Tutorial: Write iOS apps in Ruby" />
+ <meta property="og:type" content="article" />
+ <meta property="og:url" content="http://rubymotion-tutorial.com/" />
+ <meta property="og:image" content="http://i.imgur.com/mndCd.png" />
+ <meta property="og:site_name" content="RubyMotion Tutorial" />
+ <meta property="og:description" content="Learn to write iOS apps in Ruby.">
+ <meta property="fb:admins" content="1398782310" />
+ <meta property="fb:app_id" content="340990539314215" />
+
+ <meta name=viewport content="width=device-width, initial-scale=1.0,maximum-scale = 1.0">
+
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap-responsive.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/blog/pygments.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/patch.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/chapter.css'>
+
+
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-33404343-1']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+ </script>
+ </head>
+
+ <body>
+ <a id="fork-me" href="https://github.com/clayallsopp/rubymotion-tutorial" target="_blank">Fork me on GitHub</a>
+ <div class="container-fluid">
+ <div class="row-fluid">
+ <div class="span2" id="derp">
+ <nav>
+ <ul class="nav nav-list">
+ <li class="nav-header" id="chapter-nav-btn">
+ Chapters <i class="icon-chevron-up" id="chapter-nav-chevron"></i>
+ </li>
+ </ul>
+ <ul class="nav nav-list" id="chapter-nav">
+
+ <li class="active" >
+ <a href="/0-in-motion">In Motion</a>
+ </li>
+
+ <li >
+ <a href="/1-hello-motion">Hello Motion</a>
+ </li>
+
+ <li >
+ <a href="/2-views">Views</a>
+ </li>
+
+ <li >
+ <a href="/3-controllers">Controllers</a>
+ </li>
+
+ <li >
+ <a href="/4-containers">Containers</a>
+ </li>
+
+ <li >
+ <a href="/5-tables">Tables</a>
+ </li>
+
+ <li >
+ <a href="/6-animations">Animations</a>
+ </li>
+
+ <li >
+ <a href="/7-models">Models</a>
+ </li>
+
+ <li >
+ <a href="/8-testing">Testing</a>
+ </li>
+
+ <li >
+ <a href="/9-http">HTTP</a>
+ </li>
+
+ <li >
+ <a href="/10-api-driven-example">API Driven Example</a>
+ </li>
+
+ </ul>
+ <ul class="nav nav-list">
+ <li class="divider"></li>
+ <li><a href="/">Home</a></li>
+ <li><a href="http://github.com/clayallsopp/rubymotion-tutorial">Source</a></li>
+ <li class="divider"></li>
+ </ul>
+ </nav>
+ </div>
+ <div class="span10" id="content">
+ <div id="main">
+ <h1 id="toc_0">Prologue: In Motion</h1>
+
+<h2 id="toc_1">In The Beginning</h2>
+
+<p>Over the past five years, the amount and variation of products created on iOS has exceeded everyone&#39;s expectations; however, the process of iOS development has remained fundamentally unchanged. There have been welcome improvements to the Objective-C language (blocks, ARC, literals), but it&#39;s clear that Apple intends to stick with it for the long haul.</p>
+
+<p>And that&#39;s not a bad thing at all. Objective-C is fantastic for what it wants to be: a dynamic, object-oriented superset of C. It can do compile-time type-checking while still providing introspection and reflection at runtime, which lets you do some magical yet stable tricks with your code.</p>
+
+<p>But despite its strengths, many programmers balk at the prospect of learning a niche and odd-looking language just to make iOS software. Instead, they turn to familiar yet non-native technologies like HTML5, which ultimately produce a substandard user experience. Wouldn&#39;t it be wonderful if there was a more accessible language with which we could write native, performant iOS apps? <em>dun dun dun</em></p>
+
+<h2 id="toc_2">And Then There Was Motion</h2>
+
+<p><a href="http://rubymotion.com">RubyMotion</a> is such a solution. It allows you to write iOS apps in <a href="http://www.ruby-lang.org/en/">Ruby</a> with no penalty to user experience. And it preserves the iOS SDK exactly as intended by Apple, so all exisiting code examples and tutorials are perfectly translateable.</p>
+
+<p>How does that work? Well, technically it&#39;s a <em>variant</em> of Ruby; you can&#39;t use some of Ruby&#39;s extremely dynamic methods like <code>eval</code>, and it adds some new features like <code>functions.with(some, named: parameters)</code>. This allows your Ruby code to be compiled to machine code identical to Objective-C; in other words, the device can&#39;t tell the difference between RubyMotion and normal iOS apps.</p>
+
+<p>If you just don&#39;t like Ruby, RubyMotion won&#39;t change your mind. But if you aren&#39;t totally anti-Ruby, or you&#39;re just a developer who wants to write apps in a language farther away from the metal, I highly recommend you give RubyMotion a chance.</p>
+
+<p><a href="/1-hello-motion">Head to the first chapter and get started!</a></p>
+
+ </div>
+ <hr />
+ <h2>Like it?<small id="spread"> Spread the word</small></h2>
+ <div id="social">
+ <a href="https://twitter.com/share" class="twitter-share-button" data-url="http://rubymotion-tutorial.com" data-text="RubyMotion Tutorial: Make iOS Apps With Ruby" data-size="large" data-related="clayallsopp" data-count="none">Tweet</a>
+ <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
+
+ <iframe id="facebook-like" src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Frubymotion-tutorial.com&amp;send=false&amp;layout=standard&amp;width=480&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font&amp;height=35&amp;appId=340990539314215" scrolling="no" frameborder="0" allowTransparency="true"></iframe>
+ </div>
+ <hr />
+ </div>
+ </div>
+ </div>
+
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
+ <script src='http://clayallsopp.github.com/rubymotion-tutorial/js/bootstrap.min.js' type="text/javascript"></script>
+ <script src='http://clayallsopp.github.com/rubymotion-tutorial/js/chapters.js' type="text/javascript"></script>
+ </body>
+</html>
View
252 1-hello-motion/index.html
@@ -0,0 +1,252 @@
+
+<!doctype html>
+ <head>
+ <title> Hello Motion | RubyMotion Tutorial</title>
+ <meta name="description" content="Learn the installation and project setup of RubyMotion, then make your first iOS app" />
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="all" />
+ <meta name="MSSmartTagsPreventParsing" content="true" />
+ <meta name="author" content="Clay Allsopp" />
+
+ <meta property="og:title" content="RubyMotion Tutorial: Write iOS apps in Ruby" />
+ <meta property="og:type" content="article" />
+ <meta property="og:url" content="http://rubymotion-tutorial.com/" />
+ <meta property="og:image" content="http://i.imgur.com/mndCd.png" />
+ <meta property="og:site_name" content="RubyMotion Tutorial" />
+ <meta property="og:description" content="Learn to write iOS apps in Ruby.">
+ <meta property="fb:admins" content="1398782310" />
+ <meta property="fb:app_id" content="340990539314215" />
+
+ <meta name=viewport content="width=device-width, initial-scale=1.0,maximum-scale = 1.0">
+
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap-responsive.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/blog/pygments.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/patch.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/chapter.css'>
+
+
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-33404343-1']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+ </script>
+ </head>
+
+ <body>
+ <a id="fork-me" href="https://github.com/clayallsopp/rubymotion-tutorial" target="_blank">Fork me on GitHub</a>
+ <div class="container-fluid">
+ <div class="row-fluid">
+ <div class="span2" id="derp">
+ <nav>
+ <ul class="nav nav-list">
+ <li class="nav-header" id="chapter-nav-btn">
+ Chapters <i class="icon-chevron-up" id="chapter-nav-chevron"></i>
+ </li>
+ </ul>
+ <ul class="nav nav-list" id="chapter-nav">
+
+ <li >
+ <a href="/0-in-motion">In Motion</a>
+ </li>
+
+ <li class="active" >
+ <a href="/1-hello-motion">Hello Motion</a>
+ </li>
+
+ <li >
+ <a href="/2-views">Views</a>
+ </li>
+
+ <li >
+ <a href="/3-controllers">Controllers</a>
+ </li>
+
+ <li >
+ <a href="/4-containers">Containers</a>
+ </li>
+
+ <li >
+ <a href="/5-tables">Tables</a>
+ </li>
+
+ <li >
+ <a href="/6-animations">Animations</a>
+ </li>
+
+ <li >
+ <a href="/7-models">Models</a>
+ </li>
+
+ <li >
+ <a href="/8-testing">Testing</a>
+ </li>
+
+ <li >
+ <a href="/9-http">HTTP</a>
+ </li>
+
+ <li >
+ <a href="/10-api-driven-example">API Driven Example</a>
+ </li>
+
+ </ul>
+ <ul class="nav nav-list">
+ <li class="divider"></li>
+ <li><a href="/">Home</a></li>
+ <li><a href="http://github.com/clayallsopp/rubymotion-tutorial">Source</a></li>
+ <li class="divider"></li>
+ </ul>
+ </nav>
+ </div>
+ <div class="span10" id="content">
+ <div id="main">
+ <h1 id="toc_3">Hello Motion</h1>
+
+<h2 id="toc_4">Installation</h2>
+
+<p><a href="http://www.rubymotion.com/">RubyMotion</a> is a commercial product from HipByte, a company founded by the incredible guys responsible for <a href="http://macruby.org/">MacRuby</a>. When you <a href="http://sites.fastspring.com/hipbyte/product/rubymotion">purchase a license</a> to RubyMotion, you will receive a key and an installer app which will take care of everything. You also need to get <a href="http://itunes.apple.com/us/app/xcode/id497799835?mt=12">Xcode</a> from the Mac App Store. Xcode installs some developer tools RubyMotion relies on (such as the iOS Simulator), but you don&#39;t actually make RubyMotion projects inside the IDE.</p>
+
+<p>Instead, RubyMotion relies on command-line tools and you&#39;re welcome to use any text editor of your choice. There are <a href="http://www.rubymotion.com/developer-center/articles/editors/">addons</a> for many popular editors which help with things like code completion and build integration. RubyMotion also builds on top of existing Ruby tools like RubyGems and Rake, so if you&#39;re coming from a Ruby background you should feel right at home.</p>
+
+<p>So once everything is installed, you&#39;re ready to take the dive. Read on!</p>
+
+<h2 id="toc_5">The First App</h2>
+
+<p>Open your terminal and navigate to where you want to create your RubyMotion projects. Run:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="n">motion</span> <span class="n">create</span> <span class="no">HelloMotion</span>
+</code></pre>
+</div>
+
+<p>The <code>motion</code> command is one half of the RubyMotion toolbox; it&#39;s equivalent to the <code>rails</code> command, if you&#39;re familiar with that. It handles managing projects and the RubyMotion tools themselves (from time to time, you may be reminded to run <code>motion update</code>).</p>
+
+<p><code>motion create</code> makes a <code>HelloMotion</code> folder and some files inside, so go ahead and <code>cd</code> into it (<code>cd ./HelloMotion</code>). You&#39;ll run all of the subsequent commands from this location, so definitely keep a terminal window/tab open to it.</p>
+
+<p>We&#39;ll talk about just two of the files it creates: <code>Rakefile</code> and <code>./app/app_delegate.rb</code>.</p>
+
+<p><code>Rakefile</code> is where you handle your app configuation (stuff like what the app&#39;s named, what resources to include, etc) and library imports (so 3rd-party gems or other local sources). It&#39;s used by the other half of the RubyMotion workflow, the <code>rake</code> command. As of RubyMotion 1.11, <code>Rakefile</code> will be generated to look like:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="vg">$:</span><span class="o">.</span><span class="n">unshift</span><span class="p">(</span><span class="s2">&quot;/Library/RubyMotion/lib&quot;</span><span class="p">)</span>
+<span class="nb">require</span> <span class="s1">&#39;motion/project&#39;</span>
+
+<span class="no">Motion</span><span class="o">::</span><span class="no">Project</span><span class="o">::</span><span class="no">App</span><span class="o">.</span><span class="n">setup</span> <span class="k">do</span> <span class="o">|</span><span class="n">app</span><span class="o">|</span>
+ <span class="c1"># Use `rake config&#39; to see complete project settings.</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;HelloMotion&#39;</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>If you&#39;re not intimately familar with Ruby, the first thing you may think is, &quot;Wait...<code>$.unshift</code> who?&quot; Strange-looking indeed. What this line does is tell Ruby, &quot;When we use <code>require</code>s, also look in the &#39;/Library/RubyMotion/lib&#39; directory to find what we&#39;re requiring&quot;. &#39;motion/project&#39; resides there, and without the initial <code>$.unshift</code> nothing would be found!</p>
+
+<p>So we <code>require &#39;motion/project&#39;</code>, which gives us proper access to RubyMotion and setting up our app in the <code>.setup</code> block. There are all sorts of properties for <code>app</code>, which as the auto-generated comment says can be listed using <code>rake config</code>. By default, RubyMotion sets the <code>.name</code> to our project&#39;s name, so that looks good.</p>
+
+<p>Wait, why do we have a <code>Rakefile</code> at all? Well, RubyMotion uses <code>rake</code> for all of its functions, and the <code>rake</code> command runs the <code>Rakefile</code> in directory you run it in. The <code>Rakefile</code> is supposed to define the set of &quot;tasks&quot; which can be attached to rake (<code>rake &lt;task&gt;</code>), but these are actually created for us when we <code>require &quot;motion/project&quot;</code>.</p>
+
+<p>Give it a go! Run <code>rake</code> in your terminal and you should have a blank iPhone simulator pop up. Additionally, your terminal is now running an interactive console in which you can execute new code on the fly.</p>
+
+<p><img src="images/0.png" alt="hello"></p>
+
+<p>&quot;Hooray!&quot; you may exclaim...but how did that happen? How did we get from <code>app.name = &#39;HelloMotion&#39;</code> to an iPhone popping up?</p>
+
+<p>Turns out RubyMotion&#39;s <code>App</code> object has some sensible defaults, such as (most importantly) recursively including all Ruby files in <code>./app</code>. Remember that <code>app_delegate.rb</code> I mentioned earlier? Turns out that guy got included when we compiled our app! Let&#39;s take a look:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">class</span> <span class="nc">AppDelegate</span>
+ <span class="k">def</span> <span class="nf">application</span><span class="p">(</span><span class="n">application</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span><span class="ss">:launchOptions</span><span class="p">)</span>
+ <span class="kp">true</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>Hmm, all that did was define an <code>AppDelegate</code> class with one method. There&#39;s not even a superclass, how does this do anything?!</p>
+
+<p>Well, run that <code>rake config</code> command real quick. It&#39;ll spit out a bunch of settings and information, but what we&#39;re interested in is this:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="o">.</span><span class="n">.</span><span class="o">.</span>
+<span class="n">delegate_class</span> <span class="p">:</span> <span class="s2">&quot;AppDelegate&quot;</span>
+<span class="o">.</span><span class="n">.</span><span class="o">.</span>
+</code></pre>
+</div>
+
+<p>Whoa buddy, there&#39;s our &quot;unimportant&quot; AppDelegate! RubyMotion actually looks for a class with the name we assign as <code>delegate_class</code> and uses that in launching our app. We could&#39;ve called our class <code>SuperAppDelegate</code> and changed our <code>Rakefile</code> as such:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="no">Motion</span><span class="o">::</span><span class="no">Project</span><span class="o">::</span><span class="no">App</span><span class="o">.</span><span class="n">setup</span> <span class="k">do</span> <span class="o">|</span><span class="n">app</span><span class="o">|</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;HelloMotion&#39;</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">delegate_class</span> <span class="o">=</span> <span class="s2">&quot;SuperAppDelegate&quot;</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>So...what is a delegate? In iOS-land, when the user launches our app the system sets up a bunch of stuff for us. We need to give the OS an object which can respond to different events during that process; we refer to that object as the &quot;application delegate&quot;. It gets callbacks for when the app starts, ends, goes to the background, gets a push notification, all sorts of fun stuff.</p>
+
+<p>In the <code>motion</code> generated code, we only implement <code>def application(application, didFinishLaunchingWithOptions: launchOptions)</code>. It looks a little different than normal Ruby because of the <code>didFinishLaunchingWithOptions</code> label shoved in the middle...what&#39;s up with that? Well, time for a short history lesson.</p>
+
+<p>In most languages, functions look like this: <code>obj.makeBox(origin, size)</code>. This can be a bit of a pain because now you need to look up the implementation of that function and figure out what variables go where. Objective-C uses &quot;named parameters&quot; to solve this problem. In Objective-C, that same function looks like this: <code>[obj makeBoxWithOrigin: origin andSize: size];</code>. See how each variable directly proceeds the part of the function name which refers to it, removing ambiguity? Pretty clever. We refer to those functions by putting colons in place of variable names, like so: <code>makeBoxWithOrigin:andSize:</code>.</p>
+
+<p>In Ruby, named arguments don&#39;t exist; it has traditional methods like <code>obj.make_box(origin, size)</code>. RubyMotion decided to add named arguments to its implementation of Ruby so the original Apple APIs were compatible, like so: <code>obj.makeBox(origin, andSize: size)</code>. That <code>andSize</code> isn&#39;t just sugar; it&#39;s a real part of the method name. <code>obj.call_method(var1, withParam: var2)</code> is totally different than <code>obj.call_method(var1, withMagic: var2)</code>, despite their normal Ruby forms looking like <code>obj.call_method</code>.</p>
+
+<p>Anyway, back to the main plot. <code>application:didFinishLaunchingWithOptions:</code> is called when the system finishes setting up the app and becomes ready for us to do our own setup:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">def</span> <span class="nf">application</span><span class="p">(</span><span class="n">application</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span><span class="ss">:launchOptions</span><span class="p">)</span>
+ <span class="kp">true</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>For now, just assume it will always return <code>true</code>. Some apps may want to use <code>launchOptions</code> to determine if the app should be started or not, but most of the time you won&#39;t do that.</p>
+
+<p>Now we understand how our app boots up and have our entry point. Let&#39;s...do something? Change your <code>application:didFinishLaunchingWithOptions:</code> to look like:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">def</span> <span class="nf">application</span><span class="p">(</span><span class="n">application</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span><span class="ss">:launchOptions</span><span class="p">)</span>
+ <span class="n">alert</span> <span class="o">=</span> <span class="no">UIAlertView</span><span class="o">.</span><span class="n">new</span>
+ <span class="n">alert</span><span class="o">.</span><span class="n">message</span> <span class="o">=</span> <span class="s2">&quot;Hello Motion!&quot;</span>
+ <span class="n">alert</span><span class="o">.</span><span class="n">show</span>
+
+ <span class="nb">puts</span> <span class="s2">&quot;Hello again!&quot;</span>
+
+ <span class="kp">true</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>See what we added? First, those three lines about a <code>UIAlertView</code>. <code>UIAlertView</code>s are the blue popups you see sometimes while using iOS (logging in to the iTunes Store, pre-iOS5 push notifications, etc). We create one, give it a message, then show it.</p>
+
+<p>Next, <code>puts</code> is your basic logging statement. You can pass it a normal string to print, or any normal object which you want to some information about.</p>
+
+<p>Run <code>rake</code> again and...BAM, an unclosable blue popup! And in your terminal window, you should see &quot;Hello again!&quot; output in the console. Not a whole lot going on, but...at least now we know how it all got there.</p>
+
+<p><img src="images/1.png" alt="hello"></p>
+
+<p>What did we learn?</p>
+
+<ul>
+<li>Create RubyMotion apps with <code>motion create &lt;ProjectName&gt;</code></li>
+<li>Configure your app and import libraries inside <code>Rakefile</code></li>
+<li>Apps need a delegate, and RubyMotion needs you to set its value (or use the default) in the <code>Rakefile</code></li>
+<li>App delegates use <code>application:didFinishLaunchingWithOptions:</code> as the first entry point.</li>
+<li>Run your app using <code>rake</code> while inside the project directory.</li>
+</ul>
+
+<p><a href="/2-views">Head to the next chapter and put some Views on the screen!</a></p>
+
+ </div>
+ <hr />
+ <h2>Like it?<small id="spread"> Spread the word</small></h2>
+ <div id="social">
+ <a href="https://twitter.com/share" class="twitter-share-button" data-url="http://rubymotion-tutorial.com" data-text="RubyMotion Tutorial: Make iOS Apps With Ruby" data-size="large" data-related="clayallsopp" data-count="none">Tweet</a>
+ <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
+
+ <iframe id="facebook-like" src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Frubymotion-tutorial.com&amp;send=false&amp;layout=standard&amp;width=480&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font&amp;height=35&amp;appId=340990539314215" scrolling="no" frameborder="0" allowTransparency="true"></iframe>
+ </div>
+ <hr />
+ </div>
+ </div>
+ </div>
+
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
+ <script src='http://clayallsopp.github.com/rubymotion-tutorial/js/bootstrap.min.js' type="text/javascript"></script>
+ <script src='http://clayallsopp.github.com/rubymotion-tutorial/js/chapters.js' type="text/javascript"></script>
+ </body>
+</html>
View
583 10-api-driven-example/index.html
@@ -0,0 +1,583 @@
+
+<!doctype html>
+ <head>
+ <title> API Driven Example | RubyMotion Tutorial</title>
+ <meta name="description" content="A complete RubyMotion example app using HTTP to access a remote API" />
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="all" />
+ <meta name="MSSmartTagsPreventParsing" content="true" />
+ <meta name="author" content="Clay Allsopp" />
+
+ <meta property="og:title" content="RubyMotion Tutorial: Write iOS apps in Ruby" />
+ <meta property="og:type" content="article" />
+ <meta property="og:url" content="http://rubymotion-tutorial.com/" />
+ <meta property="og:image" content="http://i.imgur.com/mndCd.png" />
+ <meta property="og:site_name" content="RubyMotion Tutorial" />
+ <meta property="og:description" content="Learn to write iOS apps in Ruby.">
+ <meta property="fb:admins" content="1398782310" />
+ <meta property="fb:app_id" content="340990539314215" />
+
+ <meta name=viewport content="width=device-width, initial-scale=1.0,maximum-scale = 1.0">
+
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap-responsive.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/blog/pygments.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/patch.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/chapter.css'>
+
+
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-33404343-1']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+ </script>
+ </head>
+
+ <body>
+ <a id="fork-me" href="https://github.com/clayallsopp/rubymotion-tutorial" target="_blank">Fork me on GitHub</a>
+ <div class="container-fluid">
+ <div class="row-fluid">
+ <div class="span2" id="derp">
+ <nav>
+ <ul class="nav nav-list">
+ <li class="nav-header" id="chapter-nav-btn">
+ Chapters <i class="icon-chevron-up" id="chapter-nav-chevron"></i>
+ </li>
+ </ul>
+ <ul class="nav nav-list" id="chapter-nav">
+
+ <li >
+ <a href="/0-in-motion">In Motion</a>
+ </li>
+
+ <li >
+ <a href="/1-hello-motion">Hello Motion</a>
+ </li>
+
+ <li >
+ <a href="/2-views">Views</a>
+ </li>
+
+ <li >
+ <a href="/3-controllers">Controllers</a>
+ </li>
+
+ <li >
+ <a href="/4-containers">Containers</a>
+ </li>
+
+ <li >
+ <a href="/5-tables">Tables</a>
+ </li>
+
+ <li >
+ <a href="/6-animations">Animations</a>
+ </li>
+
+ <li >
+ <a href="/7-models">Models</a>
+ </li>
+
+ <li >
+ <a href="/8-testing">Testing</a>
+ </li>
+
+ <li >
+ <a href="/9-http">HTTP</a>
+ </li>
+
+ <li class="active" >
+ <a href="/10-api-driven-example">API Driven Example</a>
+ </li>
+
+ </ul>
+ <ul class="nav nav-list">
+ <li class="divider"></li>
+ <li><a href="/">Home</a></li>
+ <li><a href="http://github.com/clayallsopp/rubymotion-tutorial">Source</a></li>
+ <li class="divider"></li>
+ </ul>
+ </nav>
+ </div>
+ <div class="span10" id="content">
+ <div id="main">
+ <h1 id="toc_30">API Driven Example: Colr</h1>
+
+<p>We&#39;re going to build a front-end for the <a href="http://www.colr.org/api.html">Colr JSON API</a>. Our users can type in a color hex code (i.e. &quot;#3B5998&quot;) and then see what tags Colr users have assigned to that color. If our user is feeling particularly adventurous, they can add a new tag!</p>
+
+<p>Let&#39;s talk about high-level architecture. We need two controllers: one for search, and one for a color. These should be wrapped inside a <code>UINavigationController</code>, our top level controller. We&#39;re also going to need some models: <code>Color</code> and <code>Tag</code>. I&#39;m going to be honest: our app won&#39;t be the next top post on Dribbble, but it will work.</p>
+
+<h2 id="toc_31">Setup</h2>
+
+<p><code>motion create Colr</code> to get our project set up, and add <code>require &#39;bubble-wrap&#39;</code> to our <code>Rakefile</code>. We&#39;re going to do a proper setup and create some folders <em>inside</em> <code>./app</code>: make <code>./app/models/</code> and <code>./app/controllers</code>.</p>
+
+<h2 id="toc_32">Models</h2>
+
+<p>First let&#39;s dig into our models. The Colr API returns its colors as JSON objects as the form:</p>
+<div class="highlight"><pre class='codehilite'><code class="text syntax">{
+ &quot;timestamp&quot;: 1285886579,
+ &quot;hex&quot;: &quot;ff00ff&quot;,
+ &quot;id&quot;: 3976,
+ &quot;tags&quot;: [{
+ &quot;timestamp&quot;: 1108110851,
+ &quot;id&quot;: 2583,
+ &quot;name&quot;: &quot;fuchsia&quot;
+ }]
+}
+</code></pre>
+</div>
+
+<p>So our <code>Color</code>s will need <code>timestamp</code>, <code>hex</code>, <code>id</code>, and <code>tags</code> properties. In particular, <code>tags</code> will represent a has-many relationship with our <code>Tag</code> objects.</p>
+
+<p>Make a <code>./app/models/color.rb</code> and fill it in with our nice model template:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">class</span> <span class="nc">Color</span>
+ <span class="no">PROPERTIES</span> <span class="o">=</span> <span class="o">[</span><span class="ss">:timestamp</span><span class="p">,</span> <span class="ss">:hex</span><span class="p">,</span> <span class="ss">:id</span><span class="p">,</span> <span class="ss">:tags</span><span class="o">]</span>
+ <span class="no">PROPERTIES</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">prop</span><span class="o">|</span>
+ <span class="kp">attr_accessor</span> <span class="n">prop</span>
+ <span class="p">}</span>
+
+ <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="nb">hash</span> <span class="o">=</span> <span class="p">{})</span>
+ <span class="nb">hash</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
+ <span class="k">if</span> <span class="no">PROPERTIES</span><span class="o">.</span><span class="n">member?</span> <span class="n">key</span><span class="o">.</span><span class="n">to_sym</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">send</span><span class="p">((</span><span class="n">key</span><span class="o">.</span><span class="n">to_s</span> <span class="o">+</span> <span class="s2">&quot;=&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">to_s</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
+ <span class="k">end</span>
+ <span class="p">}</span>
+ <span class="k">end</span>
+
+ <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+</code></pre>
+</div>
+
+<p>Pretty easy to do with the <code>PROPERTIES</code> trick. But we need to give some special treatment to the <code>tags</code> attribute, which forces it to be always an array of <code>Tag</code> objects:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+
+ <span class="k">def</span> <span class="nf">tags</span>
+ <span class="vi">@tags</span> <span class="o">||=</span> <span class="o">[]</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">tags</span><span class="o">=</span><span class="p">(</span><span class="n">tags</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">tags</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">is_a?</span> <span class="no">Hash</span>
+ <span class="n">tags</span> <span class="o">=</span> <span class="n">tags</span><span class="o">.</span><span class="n">collect</span> <span class="p">{</span> <span class="o">|</span><span class="n">tag</span><span class="o">|</span> <span class="no">Tag</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">tag</span><span class="p">)</span> <span class="p">}</span>
+ <span class="k">end</span>
+
+ <span class="n">tags</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">tag</span><span class="o">|</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">tag</span><span class="o">.</span><span class="n">is_a?</span> <span class="no">Tag</span>
+ <span class="k">raise</span> <span class="s2">&quot;Wrong class for attempted tag </span><span class="si">#{</span><span class="n">tag</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">&quot;</span>
+ <span class="k">end</span>
+ <span class="p">}</span>
+
+ <span class="vi">@tags</span> <span class="o">=</span> <span class="n">tags</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>Our custom <code>#tags</code> getter guarantees that it will return an array if no value has been given. The <code>#tags=</code> setter ensures that every object in <code>tags</code> will be an actual <code>Tag</code> object. But wait...we haven&#39;t written the <code>Tag</code> class yet!</p>
+
+<p>Create and open <code>./app/models/tag.rb</code>. As you can see from the above example, the tags returned from the API are of the form:</p>
+<div class="highlight"><pre class='codehilite'><code class="text syntax">{
+ &quot;timestamp&quot;: 1108110851,
+ &quot;id&quot;: 2583,
+ &quot;name&quot;: &quot;fuchsia&quot;
+}
+</code></pre>
+</div>
+
+<p>So in our <code>Tag</code> class, let&#39;s create our nice API-friendly model. No special overrides for this one, it can work as-is:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">class</span> <span class="nc">Tag</span>
+ <span class="no">PROPERTIES</span> <span class="o">=</span> <span class="o">[</span><span class="ss">:timestamp</span><span class="p">,</span> <span class="ss">:id</span><span class="p">,</span> <span class="ss">:name</span><span class="o">]</span>
+ <span class="no">PROPERTIES</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">prop</span><span class="o">|</span>
+ <span class="kp">attr_accessor</span> <span class="n">prop</span>
+ <span class="p">}</span>
+
+ <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="nb">hash</span> <span class="o">=</span> <span class="p">{})</span>
+ <span class="nb">hash</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
+ <span class="k">if</span> <span class="no">PROPERTIES</span><span class="o">.</span><span class="n">member?</span> <span class="n">key</span><span class="o">.</span><span class="n">to_sym</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">send</span><span class="p">((</span><span class="n">key</span><span class="o">.</span><span class="n">to_s</span> <span class="o">+</span> <span class="s2">&quot;=&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">to_s</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
+ <span class="k">end</span>
+ <span class="p">}</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<h2 id="toc_33">Controllers</h2>
+
+<p>Now that we have our models, time to start our controllers. Make <code>./app/controllers/search_controller.rb</code> and <code>./app/controllers/color_controller.rb</code>. We should give them a bare-bones implementation for now:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">class</span> <span class="nc">SearchController</span> <span class="o">&lt;</span> <span class="no">UIViewController</span>
+ <span class="k">def</span> <span class="nf">viewDidLoad</span>
+ <span class="k">super</span>
+
+ <span class="nb">self</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s2">&quot;Search&quot;</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">class</span> <span class="nc">ColorController</span> <span class="o">&lt;</span> <span class="no">UIViewController</span>
+ <span class="k">def</span> <span class="nf">viewDidLoad</span>
+ <span class="k">super</span>
+
+ <span class="nb">self</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s2">&quot;Color&quot;</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>And in our <code>AppDelegate</code>, throw a <code>UIWindow</code> and <code>UINavigationController</code> on the screen:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">class</span> <span class="nc">AppDelegate</span>
+ <span class="k">def</span> <span class="nf">application</span><span class="p">(</span><span class="n">application</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span><span class="ss">:launchOptions</span><span class="p">)</span>
+ <span class="vi">@window</span> <span class="o">=</span> <span class="no">UIWindow</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span><span class="p">(</span><span class="no">UIScreen</span><span class="o">.</span><span class="n">mainScreen</span><span class="o">.</span><span class="n">bounds</span><span class="p">)</span>
+
+ <span class="vi">@search_controller</span> <span class="o">=</span> <span class="no">SearchController</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithNibName</span><span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="n">bundle</span><span class="ss">:nil</span><span class="p">)</span>
+ <span class="vi">@navigation_controller</span> <span class="o">=</span> <span class="no">UINavigationController</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithRootViewController</span><span class="p">(</span><span class="vi">@search_controller</span><span class="p">)</span>
+
+ <span class="vi">@window</span><span class="o">.</span><span class="n">rootViewController</span> <span class="o">=</span> <span class="vi">@navigation_controller</span>
+ <span class="vi">@window</span><span class="o">.</span><span class="n">makeKeyAndVisible</span>
+ <span class="kp">true</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>We&#39;ve seen this stuff before, but maybe not all at once. <code>rake</code> and check out our spartan app:</p>
+
+<p><img src="images/0.png" alt="search title in app"></p>
+
+<p>Good start! Time to fill in our <code>SearchController</code>.</p>
+
+<h2 id="toc_34">SearchController</h2>
+
+<p>We&#39;re going to add a new type of control we haven&#39;t used, <code>UITextField</code>, to retrieve the hex code from the user. When the user pushes a &quot;Search&quot; button, we&#39;ll run the appropriate API request and lock the UI until it finishes. If we found a result, we&#39;ll push a new <code>ColorController</code>; else, we&#39;ll show a sad alert. Sound gravy?</p>
+
+<p>We can setup our views in <code>SearchController</code> using something like this:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"> <span class="k">def</span> <span class="nf">viewDidLoad</span>
+ <span class="k">super</span>
+
+ <span class="nb">self</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s2">&quot;Search&quot;</span>
+
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="no">UIColor</span><span class="o">.</span><span class="n">whiteColor</span>
+
+ <span class="vi">@text_field</span> <span class="o">=</span> <span class="no">UITextField</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span> <span class="o">[[</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="o">]</span><span class="p">,</span> <span class="o">[</span><span class="mi">160</span><span class="p">,</span> <span class="mi">26</span><span class="o">]]</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">placeholder</span> <span class="o">=</span> <span class="s2">&quot;#abcabc&quot;</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">textAlignment</span> <span class="o">=</span> <span class="no">UITextAlignmentCenter</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">autocapitalizationType</span> <span class="o">=</span> <span class="no">UITextAutocapitalizationTypeNone</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">borderStyle</span> <span class="o">=</span> <span class="no">UITextBorderStyleRoundedRect</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">center</span> <span class="o">=</span> <span class="no">CGPointMake</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="o">/</span> <span class="mi">2</span><span class="p">,</span> <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span> <span class="o">/</span> <span class="mi">2</span> <span class="o">-</span> <span class="mi">100</span><span class="p">)</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span> <span class="vi">@text_field</span>
+
+ <span class="vi">@search</span> <span class="o">=</span> <span class="no">UIButton</span><span class="o">.</span><span class="n">buttonWithType</span><span class="p">(</span><span class="no">UIButtonTypeRoundedRect</span><span class="p">)</span>
+ <span class="vi">@search</span><span class="o">.</span><span class="n">setTitle</span><span class="p">(</span><span class="s2">&quot;Search&quot;</span><span class="p">,</span> <span class="n">forState</span><span class="ss">:UIControlStateNormal</span><span class="p">)</span>
+ <span class="vi">@search</span><span class="o">.</span><span class="n">setTitle</span><span class="p">(</span><span class="s2">&quot;Loading&quot;</span><span class="p">,</span> <span class="n">forState</span><span class="ss">:UIControlStateDisabled</span><span class="p">)</span>
+ <span class="vi">@search</span><span class="o">.</span><span class="n">sizeToFit</span>
+ <span class="vi">@search</span><span class="o">.</span><span class="n">center</span> <span class="o">=</span> <span class="no">CGPointMake</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="o">/</span> <span class="mi">2</span><span class="p">,</span> <span class="vi">@text_field</span><span class="o">.</span><span class="n">center</span><span class="o">.</span><span class="n">y</span> <span class="o">+</span> <span class="mi">40</span><span class="p">)</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span> <span class="vi">@search</span>
+ <span class="k">end</span>
+</code></pre>
+</div>
+
+<p>A lot of the specific positioning (<code>self.view.frame.size.height / 2 - 100</code>) are based on my personal guess and check, there&#39;s no special magic going on (unfortunately). Some new bits are <code>UIControlStateDisabled</code>, which corresponds to what the button looks like if we do <code>@search.enabled = false</code>, and <code>UITextBorderStyleRoundedRect</code>, which is a nice-looking style of <code>UITextField</code>s.</p>
+
+<p><code>rake</code> now and get a feel for our interface:</p>
+
+<p><img src="images/1.png" alt="search controller in app"></p>
+
+<p>Time to hook it up to some events. Remember when I said <code>BubbleWrap</code> comes with some nifty wrappers? Well, it has one to replace the clumsy <code>addTarget:action:forControlEvents</code> syntax we used earlier. Check out how idiomatic Ruby can make our code much cleaner:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"> <span class="k">def</span> <span class="nf">viewDidLoad</span>
+ <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span> <span class="vi">@search</span>
+
+ <span class="vi">@search</span><span class="o">.</span><span class="n">when</span><span class="p">(</span><span class="no">UIControlEventTouchUpInside</span><span class="p">)</span> <span class="k">do</span>
+ <span class="vi">@search</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">false</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">false</span>
+
+ <span class="n">hex</span> <span class="o">=</span> <span class="vi">@text_field</span><span class="o">.</span><span class="n">text</span>
+ <span class="c1"># chop off any leading #s</span>
+ <span class="n">hex</span> <span class="o">=</span> <span class="n">hex</span><span class="o">[</span><span class="mi">1</span><span class="o">.</span><span class="n">.</span><span class="o">-</span><span class="mi">1</span><span class="o">]</span> <span class="k">if</span> <span class="n">hex</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">==</span> <span class="s2">&quot;#&quot;</span>
+
+ <span class="no">Color</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">hex</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">color</span><span class="o">|</span>
+ <span class="vi">@search</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">true</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">true</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+</code></pre>
+</div>
+
+<p>The <code>when</code> function is available to every <code>UIControl</code> subclass (of which <code>UIButton</code> is one) and takes the usual bitmask of <code>UIControlEvent</code>s as its arguments. While the request runs, we temporarily disable our UI elements.</p>
+
+<p>But wait...what&#39;s that <code>Color.find</code> method? Well, it&#39;s a good idea to keep all of your URL requests inside of models instead of controllers. That way if we want to get a <code>Color</code> from the server somewhere else in the app then there&#39;s no code duplication. Who knows, maybe we&#39;ll need that too...<em>foreshadowing</em></p>
+
+<p>So in your <code>Color</code> class, add the <code>find</code> static method:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">class</span> <span class="nc">Color</span>
+ <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+
+ <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">find</span><span class="p">(</span><span class="n">hex</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
+ <span class="no">BW</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;http://www.colr.org/json/color/</span><span class="si">#{</span><span class="n">hex</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">response</span><span class="o">|</span>
+ <span class="nb">p</span> <span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">to_str</span>
+ <span class="c1"># for now, pass nil.</span>
+ <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>Look bad? We use the basic <code>HTTP.get</code> to get some data from the server via the proper API URL. Note that we use the <code>&amp;block</code> notation to make it plain that this function is intended to be used with a block. This block isn&#39;t explicitly passed as another argument, but rather is implicitly passed when we put a <code>do/end</code> after we call the method. The number and order of variables in <code>.call(some, variables)</code> corresponds to <code>do |some, variables|</code>.</p>
+
+<p>Anyway, <code>rake</code> and give it a go with a color like &quot;3B5998&quot;. You should see something like this output in the console:</p>
+<div class="highlight"><pre class='codehilite'><code class="text syntax">(main)&gt; &quot;{\&quot;colors\&quot;: [{\&quot;timestamp\&quot;: 1285886579, \&quot;hex\&quot;: \&quot;ff00ff\&quot;, \&quot;id\&quot;: 3976, \&quot;tags\&quot;: [{\&quot;timestamp\&quot;: 1108110851, \&quot;id\&quot;: 2583, \&quot;name\&quot;: \&quot;fuchsia\&quot;}, {\&quot;timestamp\&quot;: 1108110864, \&quot;id\&quot;: 3810, \&quot;name\&quot;: \&quot;magenta\&quot;}, {\&quot;timestamp\&quot;: 1108110870, \&quot;id\&quot;: 4166, \&quot;name\&quot;: \&quot;magic\&quot;}, {\&quot;timestamp\&quot;: 1108110851, \&quot;id\&quot;: 2626, \&quot;name\&quot;: \&quot;pink\&quot;}, {\&quot;timestamp\&quot;: 1240447803, \&quot;id\&quot;: 24479, \&quot;name\&quot;: \&quot;rgba8b24ff00ff\&quot;}, {\&quot;timestamp\&quot;: 1108110864, \&quot;id\&quot;: 3810, \&quot;name\&quot;: \&quot;magenta\&quot;}]}], \&quot;schemes\&quot;: [], \&quot;schemes_history\&quot;: {}, \&quot;success\&quot;: true, \&quot;colors_history\&quot;: {\&quot;ff00ff\&quot;: [{\&quot;d_count\&quot;: 0, \&quot;id\&quot;: \&quot;4166\&quot;, \&quot;a_count\&quot;: 1, \&quot;name\&quot;: \&quot;magic\&quot;}, {\&quot;d_count\&quot;: 0, \&quot;id\&quot;: \&quot;2626\&quot;, \&quot;a_count\&quot;: 1, \&quot;name\&quot;: \&quot;pink\&quot;}, {\&quot;d_count\&quot;: 0, \&quot;id\&quot;: \&quot;24479\&quot;, \&quot;a_count\&quot;: 1, \&quot;name\&quot;: \&quot;rgba8b24ff00ff\&quot;}, {\&quot;d_count\&quot;: 0, \&quot;id\&quot;: \&quot;3810\&quot;, \&quot;a_count\&quot;: 1, \&quot;name\&quot;: \&quot;magenta\&quot;}]}, \&quot;messages\&quot;: [], \&quot;new_color\&quot;: \&quot;ff00ff\&quot;}\n&quot;
+</code></pre>
+</div>
+
+<p>Hey...that looks an awful lot like JSON, doesn&#39;t it? (if you didn&#39;t notice the &quot;/json/&quot; in the URL). Wouldn&#39;t it be great if we could parse that into a normal Ruby hash?</p>
+
+<p>BubbleWrap to the rescue again! Our nifty friend also has a <code>BW::JSON.parse</code> method which does exactly what it sounds like. Let&#39;s update <code>Color.find</code> to use it:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">find</span><span class="p">(</span><span class="n">hex</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
+ <span class="no">BW</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;http://www.colr.org/json/color/</span><span class="si">#{</span><span class="n">hex</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">response</span><span class="o">|</span>
+ <span class="n">result_data</span> <span class="o">=</span> <span class="no">BW</span><span class="o">::</span><span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">to_str</span><span class="p">)</span>
+ <span class="n">color_data</span> <span class="o">=</span> <span class="n">result_data</span><span class="o">[</span><span class="s2">&quot;colors&quot;</span><span class="o">][</span><span class="mi">0</span><span class="o">]</span>
+
+ <span class="c1"># Colr will return a color with id == -1 if no color was found</span>
+ <span class="n">color</span> <span class="o">=</span> <span class="no">Color</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">color_data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">color</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">to_i</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span>
+ <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>
+ <span class="k">else</span>
+ <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">color</span><span class="p">)</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>And in our <code>SearchController</code>, our callback can adapt appropriately if we got an invalid color:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"> <span class="k">def</span> <span class="nf">viewDidLoad</span>
+ <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+
+ <span class="no">Color</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">hex</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">color</span><span class="o">|</span>
+ <span class="k">if</span> <span class="n">color</span><span class="o">.</span><span class="n">nil?</span>
+ <span class="vi">@search</span><span class="o">.</span><span class="n">setTitle</span><span class="p">(</span><span class="s2">&quot;None :(&quot;</span><span class="p">,</span> <span class="n">forState</span><span class="p">:</span> <span class="no">UIControlStateNormal</span><span class="p">)</span>
+ <span class="k">else</span>
+ <span class="vi">@search</span><span class="o">.</span><span class="n">setTitle</span><span class="p">(</span><span class="s2">&quot;Search&quot;</span><span class="p">,</span> <span class="n">forState</span><span class="p">:</span> <span class="no">UIControlStateNormal</span><span class="p">)</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">open_color</span><span class="p">(</span><span class="n">color</span><span class="p">)</span>
+ <span class="k">end</span>
+
+ <span class="vi">@search</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">true</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">true</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">open_color</span><span class="p">(</span><span class="n">color</span><span class="p">)</span>
+ <span class="nb">p</span> <span class="s2">&quot;Opening </span><span class="si">#{</span><span class="n">color</span><span class="si">}</span><span class="s2">&quot;</span>
+ <span class="k">end</span>
+</code></pre>
+</div>
+
+<p>This seems pretty reasonable. We parse the JSON, check for the non-existent/-1 id, and alter the UI accordingly:</p>
+
+<p><img src="images/2.png" alt="search controller in app"></p>
+
+<p>Great! Let&#39;s fix that <code>open_color</code> method to work. It should push a <code>ColorController</code> with the proper color, so we should probably fill in that implementation now.</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">def</span> <span class="nf">open_color</span><span class="p">(</span><span class="n">color</span><span class="p">)</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">navigationController</span><span class="o">.</span><span class="n">pushViewController</span><span class="p">(</span><span class="no">ColorController</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithColor</span><span class="p">(</span><span class="n">color</span><span class="p">),</span> <span class="n">animated</span><span class="ss">:true</span><span class="p">)</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<h2 id="toc_35">ColorController</h2>
+
+<p>We&#39;re going to use a custom initializer for <code>ColorController</code>; these custom initializers should always call the designated initializer of their superclass first (in this case, <code>initWithNibName:bundle:</code>). The controller&#39;s view will have two parts: a <code>UITableView</code> to display the color&#39;s tags, and a section to display the color and add new tags. When we want to add a new tag, a POST request is sent and our data is refreshed accordingly.</p>
+
+<p>That sounds like a lot, so let&#39;s take it one step at a time. First, our custom initializer:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">class</span> <span class="nc">ColorController</span> <span class="o">&lt;</span> <span class="no">UIViewController</span>
+ <span class="kp">attr_accessor</span> <span class="ss">:color</span>
+
+ <span class="k">def</span> <span class="nf">initWithColor</span><span class="p">(</span><span class="n">color</span><span class="p">)</span>
+ <span class="n">initWithNibName</span><span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="n">bundle</span><span class="ss">:nil</span><span class="p">)</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">color</span> <span class="o">=</span> <span class="n">color</span>
+ <span class="nb">self</span>
+ <span class="k">end</span>
+
+ <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+</code></pre>
+</div>
+
+<p>When overriding an iOS SDK initializer, you need to do two things: call the designated initializer and return <code>self</code> at the end. Beware: you can&#39;t use the usual Ruby <code>initialize</code> function!</p>
+
+<p>Next, let&#39;s layout our interface:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"> <span class="k">def</span> <span class="nf">viewDidLoad</span>
+ <span class="k">super</span>
+
+ <span class="nb">self</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">hex</span>
+
+ <span class="c1"># A light grey background to separate the Tag table from the Color info</span>
+ <span class="vi">@info_container</span> <span class="o">=</span> <span class="no">UIView</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span> <span class="o">[[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="o">]</span><span class="p">,</span> <span class="o">[</span><span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="mi">110</span><span class="o">]]</span>
+ <span class="vi">@info_container</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="no">UIColor</span><span class="o">.</span><span class="n">lightGrayColor</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span> <span class="vi">@info_container</span>
+
+ <span class="c1"># A visual preview of the actual color</span>
+ <span class="vi">@color_view</span> <span class="o">=</span> <span class="no">UIView</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span> <span class="o">[[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="o">]</span><span class="p">,</span> <span class="o">[</span><span class="mi">90</span><span class="p">,</span> <span class="mi">90</span><span class="o">]]</span>
+ <span class="c1"># String#to_color is another handy BubbbleWrap addition!</span>
+ <span class="vi">@color_view</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="nb">String</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">hex</span><span class="p">)</span><span class="o">.</span><span class="n">to_color</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span> <span class="vi">@color_view</span>
+
+ <span class="c1"># Displays the hex code of our color</span>
+ <span class="vi">@color_label</span> <span class="o">=</span> <span class="no">UILabel</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span> <span class="o">[[</span><span class="mi">110</span><span class="p">,</span> <span class="mi">30</span><span class="o">]</span><span class="p">,</span> <span class="o">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="o">]]</span>
+ <span class="vi">@color_label</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">hex</span>
+ <span class="vi">@color_label</span><span class="o">.</span><span class="n">sizeToFit</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span> <span class="vi">@color_label</span>
+
+ <span class="c1"># Where we enter the new tag</span>
+ <span class="vi">@text_field</span> <span class="o">=</span> <span class="no">UITextField</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span> <span class="o">[[</span><span class="mi">110</span><span class="p">,</span> <span class="mi">60</span><span class="o">]</span><span class="p">,</span> <span class="o">[</span><span class="mi">100</span><span class="p">,</span> <span class="mi">26</span><span class="o">]]</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">placeholder</span> <span class="o">=</span> <span class="s2">&quot;tag&quot;</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">textAlignment</span> <span class="o">=</span> <span class="no">UITextAlignmentCenter</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">autocapitalizationType</span> <span class="o">=</span> <span class="no">UITextAutocapitalizationTypeNone</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">borderStyle</span> <span class="o">=</span> <span class="no">UITextBorderStyleRoundedRect</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span> <span class="vi">@text_field</span>
+
+ <span class="c1"># Tapping this adds the tag.</span>
+ <span class="vi">@add</span> <span class="o">=</span> <span class="no">UIButton</span><span class="o">.</span><span class="n">buttonWithType</span><span class="p">(</span><span class="no">UIButtonTypeRoundedRect</span><span class="p">)</span>
+ <span class="vi">@add</span><span class="o">.</span><span class="n">setTitle</span><span class="p">(</span><span class="s2">&quot;Add&quot;</span><span class="p">,</span> <span class="n">forState</span><span class="ss">:UIControlStateNormal</span><span class="p">)</span>
+ <span class="vi">@add</span><span class="o">.</span><span class="n">setTitle</span><span class="p">(</span><span class="s2">&quot;Adding...&quot;</span><span class="p">,</span> <span class="n">forState</span><span class="ss">:UIControlStateDisabled</span><span class="p">)</span>
+ <span class="vi">@add</span><span class="o">.</span><span class="n">setTitleColor</span><span class="p">(</span><span class="no">UIColor</span><span class="o">.</span><span class="n">lightGrayColor</span><span class="p">,</span> <span class="n">forState</span><span class="ss">:UIControlStateDisabled</span><span class="p">)</span>
+ <span class="vi">@add</span><span class="o">.</span><span class="n">sizeToFit</span>
+ <span class="vi">@add</span><span class="o">.</span><span class="n">frame</span> <span class="o">=</span> <span class="o">[[</span><span class="vi">@text_field</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">origin</span><span class="o">.</span><span class="n">x</span> <span class="o">+</span> <span class="vi">@text_field</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="o">+</span> <span class="mi">10</span><span class="p">,</span> <span class="vi">@text_field</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">origin</span><span class="o">.</span><span class="n">y</span><span class="o">]</span><span class="p">,</span>
+ <span class="vi">@add</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">size</span><span class="o">]</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span><span class="p">(</span><span class="vi">@add</span><span class="p">)</span>
+
+ <span class="c1"># The table for our color&#39;s tags.</span>
+ <span class="n">table_frame</span> <span class="o">=</span> <span class="o">[[</span><span class="mi">0</span><span class="p">,</span> <span class="vi">@info_container</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span><span class="o">]</span><span class="p">,</span>
+ <span class="o">[</span><span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span> <span class="o">-</span> <span class="vi">@info_container</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span> <span class="o">-</span> <span class="nb">self</span><span class="o">.</span><span class="n">navigationController</span><span class="o">.</span><span class="n">navigationBar</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span><span class="o">]]</span>
+ <span class="vi">@table_view</span> <span class="o">=</span> <span class="no">UITableView</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span><span class="p">(</span><span class="n">table_frame</span><span class="p">,</span> <span class="n">style</span><span class="ss">:UITableViewStylePlain</span><span class="p">)</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span><span class="p">(</span><span class="vi">@table_view</span><span class="p">)</span>
+ <span class="k">end</span>
+</code></pre>
+</div>
+
+<p>WHEW WELL THAT IS A TON OF CODE NOW ISN&#39;T IT. But again, we&#39;ve built up to this point so we&#39;ve seen it all, don&#39;t be intimidated. We just added a bunch of subviews and hooked them up to the appropriate data.</p>
+
+<p><code>rake</code> and see for yourself:</p>
+
+<p><img src="images/3.png" alt="color controller in app"></p>
+
+<p>Hey, I told you it wouldn&#39;t win any design awards.</p>
+
+<p>Time to hook up the tags. We&#39;re going to use our handy table view <code>delegate</code> methods to populate the table with the tags. It&#39;ll be just a normal list, no fancy sections or callbacks:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"> <span class="k">def</span> <span class="nf">viewDidLoad</span>
+ <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+
+ <span class="vi">@table_view</span><span class="o">.</span><span class="n">dataSource</span> <span class="o">=</span> <span class="nb">self</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">tableView</span><span class="p">(</span><span class="n">tableView</span><span class="p">,</span> <span class="n">numberOfRowsInSection</span><span class="ss">:section</span><span class="p">)</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">count</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">tableView</span><span class="p">(</span><span class="n">tableView</span><span class="p">,</span> <span class="n">cellForRowAtIndexPath</span><span class="ss">:indexPath</span><span class="p">)</span>
+ <span class="vi">@reuseIdentifier</span> <span class="o">||=</span> <span class="s2">&quot;CELL_IDENTIFIER&quot;</span>
+
+ <span class="n">cell</span> <span class="o">=</span> <span class="n">tableView</span><span class="o">.</span><span class="n">dequeueReusableCellWithIdentifier</span><span class="p">(</span><span class="vi">@reuseIdentifier</span><span class="p">)</span> <span class="o">||</span> <span class="k">begin</span>
+ <span class="no">UITableViewCell</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithStyle</span><span class="p">(</span><span class="no">UITableViewCellStyleDefault</span><span class="p">,</span> <span class="n">reuseIdentifier</span><span class="ss">:@reuseIdentifier</span><span class="p">)</span>
+ <span class="k">end</span>
+
+ <span class="n">cell</span><span class="o">.</span><span class="n">textLabel</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">tags</span><span class="o">[</span><span class="n">indexPath</span><span class="o">.</span><span class="n">row</span><span class="o">].</span><span class="n">name</span>
+
+ <span class="n">cell</span>
+ <span class="k">end</span>
+</code></pre>
+</div>
+
+<p>Another<code>rake</code> and hey! Some interesting data!</p>
+
+<p><img src="images/4.png" alt="color tags in app"></p>
+
+<p>And now one more thing: adding new tags. There are a couple of ways to organize this new feature, like <code>Tag.create(tag)</code> or magically hack into <code>color.tags &lt;&lt; tag</code>, but we&#39;re going to go with <code>color.add_tag(tag, &amp;block)</code>. Why? Because it shows that tags and colors are tightly coupled.</p>
+
+<p>Here&#39;s what that <code>add_tag</code> method looks like:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"> <span class="k">def</span> <span class="nf">add_tag</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
+ <span class="no">BW</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;http://www.colr.org/js/color/</span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">hex</span><span class="si">}</span><span class="s2">/addtag/&quot;</span><span class="p">,</span> <span class="n">payload</span><span class="p">:</span> <span class="p">{</span><span class="n">tags</span><span class="p">:</span> <span class="n">tag</span><span class="p">})</span> <span class="k">do</span> <span class="o">|</span><span class="n">response</span><span class="o">|</span>
+ <span class="n">block</span><span class="o">.</span><span class="n">call</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+</code></pre>
+</div>
+
+<p>Here we don&#39;t pass any extra arguments to <code>block.call</code>. We could pass some designation of success or failure, but we don&#39;t have to be super fault tolerant for this example.</p>
+
+<p>Now we place the <code>add_tag</code> code inside our button callback in <code>ColorController</code>. After the tag is sent to the server, we want to refresh our color with the current server data to make absolutely sure that the server received our tag. So, let&#39;s add that <code>when</code> callback:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"> <span class="k">def</span> <span class="nf">viewDidLoad</span>
+ <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">addSubview</span><span class="p">(</span><span class="vi">@add</span><span class="p">)</span>
+
+ <span class="vi">@add</span><span class="o">.</span><span class="n">when</span><span class="p">(</span><span class="no">UIControlEventTouchUpInside</span><span class="p">)</span> <span class="k">do</span>
+ <span class="vi">@add</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">false</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">false</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">add_tag</span><span class="p">(</span><span class="vi">@text_field</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> <span class="k">do</span>
+ <span class="n">refresh</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+
+ <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">refresh</span>
+ <span class="no">Color</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">hex</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">color</span><span class="o">|</span>
+ <span class="nb">self</span><span class="o">.</span><span class="n">color</span> <span class="o">=</span> <span class="n">color</span>
+
+ <span class="vi">@table_view</span><span class="o">.</span><span class="n">reloadData</span>
+
+ <span class="vi">@add</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">true</span>
+ <span class="vi">@text_field</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">true</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+</code></pre>
+</div>
+
+<p>Let&#39;s walk through this. We add our <code>UIControlEventTouchUpInside</code> callback to <code>@add</code>, which calls <code>color.add_tag</code> and runs the appropriate POST request. When that request finishes, we call a new <code>refresh</code> method. This will run the normal <code>Color.find</code> request and reset our data.</p>
+
+<p>Give it a <code>rake</code> and add a tag. Should go swimmingly.</p>
+
+<p><img src="images/5.png" alt="adding a tag"></p>
+
+<h2 id="toc_36">Wrapping Up</h2>
+
+<p>Whew, that&#39;s a giant example. It&#39;s decently architected and demonstrates one way of separating responsibility between models and controllers. We could&#39;ve done more with the views, maybe adding some KVO, but for such a small example it would&#39;ve been overkill.</p>
+
+<p>What should we take away from this?</p>
+
+<ul>
+<li>Use models to represent your data, don&#39;t use the hashes you get returned from <code>JSON.parse</code>.</li>
+<li>Run your URL requests in models.</li>
+<li>Use your controllers to respond to callbacks and user events.</li>
+<li>Keep the interface responsive by disabling or changing the UI while time consuming requests are running.</li>
+</ul>
+
+ </div>
+ <hr />
+ <h2>Like it?<small id="spread"> Spread the word</small></h2>
+ <div id="social">
+ <a href="https://twitter.com/share" class="twitter-share-button" data-url="http://rubymotion-tutorial.com" data-text="RubyMotion Tutorial: Make iOS Apps With Ruby" data-size="large" data-related="clayallsopp" data-count="none">Tweet</a>
+ <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
+
+ <iframe id="facebook-like" src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Frubymotion-tutorial.com&amp;send=false&amp;layout=standard&amp;width=480&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font&amp;height=35&amp;appId=340990539314215" scrolling="no" frameborder="0" allowTransparency="true"></iframe>
+ </div>
+ <hr />
+ </div>
+ </div>
+ </div>
+
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
+ <script src='http://clayallsopp.github.com/rubymotion-tutorial/js/bootstrap.min.js' type="text/javascript"></script>
+ <script src='http://clayallsopp.github.com/rubymotion-tutorial/js/chapters.js' type="text/javascript"></script>
+ </body>
+</html>
View
232 2-views/index.html
@@ -0,0 +1,232 @@
+
+<!doctype html>
+ <head>
+ <title> Views | RubyMotion Tutorial</title>
+ <meta name="description" content="Learn about UIViews and the RubyMotion interactive console" />
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="all" />
+ <meta name="MSSmartTagsPreventParsing" content="true" />
+ <meta name="author" content="Clay Allsopp" />
+
+ <meta property="og:title" content="RubyMotion Tutorial: Write iOS apps in Ruby" />
+ <meta property="og:type" content="article" />
+ <meta property="og:url" content="http://rubymotion-tutorial.com/" />
+ <meta property="og:image" content="http://i.imgur.com/mndCd.png" />
+ <meta property="og:site_name" content="RubyMotion Tutorial" />
+ <meta property="og:description" content="Learn to write iOS apps in Ruby.">
+ <meta property="fb:admins" content="1398782310" />
+ <meta property="fb:app_id" content="340990539314215" />
+
+ <meta name=viewport content="width=device-width, initial-scale=1.0,maximum-scale = 1.0">
+
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap-responsive.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/blog/pygments.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/patch.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/chapter.css'>
+
+
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-33404343-1']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+ </script>
+ </head>
+
+ <body>
+ <a id="fork-me" href="https://github.com/clayallsopp/rubymotion-tutorial" target="_blank">Fork me on GitHub</a>
+ <div class="container-fluid">
+ <div class="row-fluid">
+ <div class="span2" id="derp">
+ <nav>
+ <ul class="nav nav-list">
+ <li class="nav-header" id="chapter-nav-btn">
+ Chapters <i class="icon-chevron-up" id="chapter-nav-chevron"></i>
+ </li>
+ </ul>
+ <ul class="nav nav-list" id="chapter-nav">
+
+ <li >
+ <a href="/0-in-motion">In Motion</a>
+ </li>
+
+ <li >
+ <a href="/1-hello-motion">Hello Motion</a>
+ </li>
+
+ <li class="active" >
+ <a href="/2-views">Views</a>
+ </li>
+
+ <li >
+ <a href="/3-controllers">Controllers</a>
+ </li>
+
+ <li >
+ <a href="/4-containers">Containers</a>
+ </li>
+
+ <li >
+ <a href="/5-tables">Tables</a>
+ </li>
+
+ <li >
+ <a href="/6-animations">Animations</a>
+ </li>
+
+ <li >
+ <a href="/7-models">Models</a>
+ </li>
+
+ <li >
+ <a href="/8-testing">Testing</a>
+ </li>
+
+ <li >
+ <a href="/9-http">HTTP</a>
+ </li>
+
+ <li >
+ <a href="/10-api-driven-example">API Driven Example</a>
+ </li>
+
+ </ul>
+ <ul class="nav nav-list">
+ <li class="divider"></li>
+ <li><a href="/">Home</a></li>
+ <li><a href="http://github.com/clayallsopp/rubymotion-tutorial">Source</a></li>
+ <li class="divider"></li>
+ </ul>
+ </nav>
+ </div>
+ <div class="span10" id="content">
+ <div id="main">
+ <h1 id="toc_6">Views</h1>
+
+<p>Now that we&#39;ve gotten a project running, let&#39;s start putting things on the screen.</p>
+
+<p>The &quot;stuff&quot; that&#39;s displayed in an app are called &quot;views&quot;. A view can have many &quot;subviews&quot;; when you move a view, you also move its subviews. Subviews are also visually stacked on top of each other within their parent.</p>
+
+<p>In code, these are <code>UIView</code> objects; everything you see on the screen is a descendent of <code>UIView</code>. They have a lot of nifty features, but what you should be concerned about right now is that they have:</p>
+
+<ol>
+<li>A <code>frame</code> property, which contains a view&#39;s <code>x</code> and <code>y</code> coordinates and its <code>width</code> and <code>height</code> dimensions.</li>
+<li>A <code>subviews</code> property, which is an array of all the view&#39;s subviews, sorted by back-to-front visibility (as in the index of a view in <code>subviews</code> is its &quot;z-order&quot;).</li>
+</ol>
+
+<p>A view&#39;s <code>frame</code> describes its shape and position relative to its parent view. That&#39;s a lot of words, so let me show you a quick example: imagine I have a view at (10, 10). I want a new subview of that view to appear at (50, 50) on the screen, so I have to set my new view&#39;s frame to be positioned at (40, 40). Make sense?</p>
+
+<p>The base view of an app is the &quot;window&quot;; all other views are direct or descendent subviews of the window. Your app&#39;s window is a subclass of <code>UIView</code> called <code>UIWindow</code>.</p>
+
+<p>Let&#39;s get to some code. In <code>AppDelegate</code>, change our <code>didFinishLaunching</code> method to look like:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">def</span> <span class="nf">application</span><span class="p">(</span><span class="n">application</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span><span class="ss">:launchOptions</span><span class="p">)</span>
+ <span class="c1"># UIScreen describes the display our app is running on</span>
+ <span class="vi">@window</span> <span class="o">=</span> <span class="no">UIWindow</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span><span class="p">(</span><span class="no">UIScreen</span><span class="o">.</span><span class="n">mainScreen</span><span class="o">.</span><span class="n">bounds</span><span class="p">)</span>
+ <span class="vi">@window</span><span class="o">.</span><span class="n">makeKeyAndVisible</span>
+
+ <span class="vi">@blue_view</span> <span class="o">=</span> <span class="no">UIView</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span><span class="p">(</span><span class="no">CGRectMake</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">))</span>
+ <span class="vi">@blue_view</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="no">UIColor</span><span class="o">.</span><span class="n">blueColor</span>
+ <span class="vi">@window</span><span class="o">.</span><span class="n">addSubview</span><span class="p">(</span><span class="vi">@blue_view</span><span class="p">)</span>
+
+ <span class="kp">true</span>
+<span class="k">end</span>
+</code></pre>
+</div>
+
+<p>Let&#39;s walk this out. We create a <code>UIWindow</code> instance with the dimensions of the screen and do something called <code>makeKeyAndVisible</code>. This basically tells the OS that this window will be the one receiving touch events and that it should become visible on the screen. (If you&#39;re curious, you can have multiple windows on multiple screens, but let&#39;s take it one small step at a time).</p>
+
+<p>So we create a window, then we create a new view and add it as a subview to the window. The view&#39;s frame is actually stored as a <code>CGRect</code> object, generated using <code>CGRectMake(x, y, w, h)</code>. BUT WATCH OUT! A <code>CGRect</code> is actually a composition of two objects: a <code>CGPoint</code> and <code>CGSize</code>. So if you want to read the y-coordinate or height of a view, you do <code>view.frame.position.y</code> or <code>view.frame.size.height</code>.</p>
+
+<p>Also shown is <code>UIColor</code>, which is how we play with...colors. It has some obvious defaults (<code>blueColor</code>, <code>redColor</code>, etc), but can also be used to create arbitrary tints.</p>
+
+<p><strong>FULL DISCLOSURE</strong>: adding lone <code>UIView</code>s directly to the <code>UIWindow</code> is generally not a great idea and is frowned upon, but it&#39;s a nice way to learn. So don&#39;t do it in production code.</p>
+
+<p>Run our app (just <code>rake</code>, remember?) and observe our...blue box.</p>
+
+<p><img src="images/1.png" alt="blue box"></p>
+
+<p>Let&#39;s make it more exciting by...adding more boxes! Like so:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+ <span class="vi">@blue_view</span> <span class="o">=</span> <span class="no">UIView</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span><span class="p">(</span><span class="no">CGRectMake</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">))</span>
+ <span class="vi">@blue_view</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="no">UIColor</span><span class="o">.</span><span class="n">blueColor</span>
+ <span class="vi">@window</span><span class="o">.</span><span class="n">addSubview</span><span class="p">(</span><span class="vi">@blue_view</span><span class="p">)</span>
+
+ <span class="vi">@green_view</span> <span class="o">=</span> <span class="no">UIView</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span><span class="p">(</span><span class="no">CGRectMake</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">40</span><span class="p">))</span>
+ <span class="vi">@green_view</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="no">UIColor</span><span class="o">.</span><span class="n">greenColor</span>
+ <span class="vi">@window</span><span class="o">.</span><span class="n">addSubview</span><span class="p">(</span><span class="vi">@green_view</span><span class="p">)</span>
+
+ <span class="vi">@red_view</span> <span class="o">=</span> <span class="no">UIView</span><span class="o">.</span><span class="n">alloc</span><span class="o">.</span><span class="n">initWithFrame</span><span class="p">(</span><span class="no">CGRectMake</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">40</span><span class="p">))</span>
+ <span class="vi">@red_view</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="no">UIColor</span><span class="o">.</span><span class="n">redColor</span>
+ <span class="c1"># NOTE: *not* adding to @window</span>
+ <span class="vi">@blue_view</span><span class="o">.</span><span class="n">addSubview</span><span class="p">(</span><span class="vi">@red_view</span><span class="p">)</span>
+ <span class="o">.</span><span class="n">.</span><span class="o">.</span>
+</code></pre>
+</div>
+
+<p><code>rake</code> again and a-ha! See how the red view is further down than the green view, despite seemingly identical <code>frame</code>s?</p>
+
+<p><img src="images/2.png" alt="more boxes"></p>
+
+<p>Just for fun, let&#39;s play with our interactive <code>REPL</code>. The terminal where you ran <code>rake</code> should be displaying an <code>irb</code>-esque prompt while the simulator is open. Let&#39;s dig around and find our <code>@blue_view</code>:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="o">&gt;</span> <span class="n">delegate</span> <span class="o">=</span> <span class="no">UIApplication</span><span class="o">.</span><span class="n">sharedApplication</span><span class="o">.</span><span class="n">delegate</span>
+<span class="o">=&gt;</span> <span class="c1">#&lt;AppDelegate&gt;</span>
+<span class="o">&gt;</span> <span class="n">blue_view</span> <span class="o">=</span> <span class="n">delegate</span><span class="o">.</span><span class="n">instance_variable_get</span><span class="p">(</span><span class="s1">&#39;@blue_view&#39;</span><span class="p">)</span>
+<span class="o">=&gt;</span> <span class="c1">#&lt;UIView&gt;</span>
+</code></pre>
+</div>
+
+<p>What did we do there? Well <code>UIApplication.sharedApplication</code> gives us the object the system uses to describe our application. There will only ever be one of these. Like I said earlier, it&#39;s been configured to use our delegate, which is found (conveniently, I know) with <code>.delegate</code>. Once we grab that, we simply use the nifty <code>instance_variable_get</code> to retrieve our view.</p>
+
+<p>We can confirm that the view has one subview with <code>blue_view.subviews.count</code>. Let&#39;s grab our <code>red_view</code> and do something maniacal: remove it.</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="o">&gt;</span> <span class="n">blue_view</span><span class="o">.</span><span class="n">subviews</span><span class="o">.</span><span class="n">count</span>
+<span class="o">=&gt;</span> <span class="mi">1</span>
+<span class="o">&gt;</span> <span class="n">red_view</span> <span class="o">=</span> <span class="n">blue_view</span><span class="o">.</span><span class="n">subviews</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span>
+<span class="o">=&gt;</span> <span class="c1">#&lt;UIView&gt;</span>
+<span class="o">&gt;</span> <span class="n">red_view</span><span class="o">.</span><span class="n">removeFromSuperview</span>
+<span class="o">=&gt;</span> <span class="c1">#&lt;UIView&gt;</span>
+</code></pre>
+</div>
+
+<p>&quot;Whoa&quot; there Neo, it disappeared. The <code>removeFromSuperview</code> method is how a view can remove itself from the screen and its parent view&#39;s <code>subviews</code> (you can confirm this by checking <code>blue_view.subviews.count</code> again).</p>
+
+<h2 id="toc_7">Wrap Up</h2>
+
+<p>This wasn&#39;t a very lengthy example, but it covers the core concepts of views: they have frames and they have subviews. This was simple, but as you&#39;ll see they get much more complicated. Thus, we need some better infrastructure for dealing with them.</p>
+
+<p>Let&#39;s recap:</p>
+
+<ul>
+<li>Everything on the screen is a <code>UIView</code>.</li>
+<li>The base view of your app is an instance of <code>UIWindow</code>, of which everything is a child or descendent view. Create this in your delegate.</li>
+<li>Views have subviews, added with <code>UIView#addSubview</code>. Successive subviews stack on top of each other visually.</li>
+</ul>
+
+<p><a href="/3-controllers">Moonwalk to the next chapter and get your hands dirty with Controllers!</a></p>
+
+ </div>
+ <hr />
+ <h2>Like it?<small id="spread"> Spread the word</small></h2>
+ <div id="social">
+ <a href="https://twitter.com/share" class="twitter-share-button" data-url="http://rubymotion-tutorial.com" data-text="RubyMotion Tutorial: Make iOS Apps With Ruby" data-size="large" data-related="clayallsopp" data-count="none">Tweet</a>
+ <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
+
+ <iframe id="facebook-like" src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Frubymotion-tutorial.com&amp;send=false&amp;layout=standard&amp;width=480&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font&amp;height=35&amp;appId=340990539314215" scrolling="no" frameborder="0" allowTransparency="true"></iframe>
+ </div>
+ <hr />
+ </div>
+ </div>
+ </div>
+
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
+ <script src='http://clayallsopp.github.com/rubymotion-tutorial/js/bootstrap.min.js' type="text/javascript"></script>
+ <script src='http://clayallsopp.github.com/rubymotion-tutorial/js/chapters.js' type="text/javascript"></script>
+ </body>
+</html>
View
236 3-controllers/index.html
@@ -0,0 +1,236 @@
+
+<!doctype html>
+ <head>
+ <title> Controllers | RubyMotion Tutorial</title>
+ <meta name="description" content="Use UIViewControllers to organize your app" />
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <meta name="robots" content="all" />
+ <meta name="MSSmartTagsPreventParsing" content="true" />
+ <meta name="author" content="Clay Allsopp" />
+
+ <meta property="og:title" content="RubyMotion Tutorial: Write iOS apps in Ruby" />
+ <meta property="og:type" content="article" />
+ <meta property="og:url" content="http://rubymotion-tutorial.com/" />
+ <meta property="og:image" content="http://i.imgur.com/mndCd.png" />
+ <meta property="og:site_name" content="RubyMotion Tutorial" />
+ <meta property="og:description" content="Learn to write iOS apps in Ruby.">
+ <meta property="fb:admins" content="1398782310" />
+ <meta property="fb:app_id" content="340990539314215" />
+
+ <meta name=viewport content="width=device-width, initial-scale=1.0,maximum-scale = 1.0">
+
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/bootstrap-responsive.min.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/blog/pygments.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/patch.css'>
+ <link rel=stylesheet href='http://clayallsopp.github.com/rubymotion-tutorial/css/chapter.css'>
+
+
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-33404343-1']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+ </script>
+ </head>
+
+ <body>
+ <a id="fork-me" href="https://github.com/clayallsopp/rubymotion-tutorial" target="_blank">Fork me on GitHub</a>
+ <div class="container-fluid">
+ <div class="row-fluid">
+ <div class="span2" id="derp">
+ <nav>
+ <ul class="nav nav-list">
+ <li class="nav-header" id="chapter-nav-btn">
+ Chapters <i class="icon-chevron-up" id="chapter-nav-chevron"></i>
+ </li>
+ </ul>
+ <ul class="nav nav-list" id="chapter-nav">
+
+ <li >
+ <a href="/0-in-motion">In Motion</a>
+ </li>
+
+ <li >
+ <a href="/1-hello-motion">Hello Motion</a>
+ </li>
+
+ <li >
+ <a href="/2-views">Views</a>
+ </li>
+
+ <li class="active" >
+ <a href="/3-controllers">Controllers</a>
+ </li>
+
+ <li >
+ <a href="/4-containers">Containers</a>
+ </li>
+
+ <li >
+ <a href="/5-tables">Tables</a>
+ </li>
+
+ <li >
+ <a href="/6-animations">Animations</a>
+ </li>
+
+ <li >
+ <a href="/7-models">Models</a>
+ </li>
+
+ <li >
+ <a href="/8-testing">Testing</a>
+ </li>
+
+ <li >
+ <a href="/9-http">HTTP</a>
+ </li>
+
+ <li >
+ <a href="/10-api-driven-example">API Driven Example</a>
+ </li>
+
+ </ul>
+ <ul class="nav nav-list">
+ <li class="divider"></li>
+ <li><a href="/">Home</a></li>
+ <li><a href="http://github.com/clayallsopp/rubymotion-tutorial">Source</a></li>
+ <li class="divider"></li>
+ </ul>
+ </nav>
+ </div>
+ <div class="span10" id="content">
+ <div id="main">
+ <h1 id="toc_8">Controllers</h1>
+
+<p>We&#39;ve done some work with views, but they are but one leg of the &quot;Model-View-Controller&quot; paradigm the iOS SDK uses. That sounds really fancy, but it&#39;s actually pretty simple.</p>
+
+<p>The idea is that in your code you should have three types of classes: views (which yup, you&#39;ve already seen), models (which represent and handle data), and....controllers.</p>
+
+<p>So, what are controllers? They&#39;re objects which act as a &quot;layer&quot; between models and views, interpreting events from the user to change the models and update the views in response. In a perfectly coded world, when you tap a button the controller intercepts that event, updates a property of the data, and changes the view to reflect the new data.</p>
+
+<p>That sounds kind of &quot;big picture&quot;, but there are some really practical reasons for controllers:</p>
+
+<ul>
+<li><strong>View reuse</strong>. Let&#39;s say we have a <code>PostView</code> which displays all the information about a <code>Post</code> (its content, author, &quot;Likes&quot;, etc). We want to use this view on a couple of different screens, such as a main feed and a user&#39;s profile feed. To stay reuseable, the <code>PostView</code> shouldn&#39;t deal with <em>how</em> it gets the information; instead, its controller should take care of that and then pass the processed data on to the view.</li>
+<li><strong>Presentation management</strong>. Sometimes we want a view to take up the entire screen, other times we want the same thing to appear in a modal box (think iPad vs iPhone). It doesn&#39;t make sense to write two identical view classes that differ only in presentation style, so we use the controllers to resize and animate our views accordingly.</li>
+</ul>
+
+<p>There&#39;s nothing technically stopping you from doing those things inside models and views, but it makes your code much more robust and easier to manage if you embrace MVC.</p>
+
+<p>In iOS-land, controllers are <code>UIViewController</code>s. They come with one <code>view</code> property and methods for dealing with things like the view &quot;lifecycle&quot; and handling orientation changes. Don&#39;t fret, we&#39;ll get to the lifecycle business soon enough.</p>
+
+<p>So now that we know what a controller is and what it should do, what <em>shouldn&#39;t</em> it do?</p>
+
+<ul>
+<li>Directly query or save data. It&#39;s tempting to send a bunch of HTTP requests in a controller, but those are best left to your models.</li>
+<li>Complex view layouts. If you&#39;re directly adding subviews more then one level &quot;deep&quot; to your controller&#39;s <code>view</code>, you should rewrite your views to do it themselves. As a good rule of thumb, the only <code>addSubview</code> you should see in your controller is <code>self.view.addSubview</code>.</li>
+</ul>
+
+<p>OK that&#39;s enough exposition, time for the Michael Bay action sequences.</p>
+
+<h2 id="toc_9">Everything Is Under Controllers</h2>
+
+<p>Create the <code>./app/controllers</code> directory (<code>mkdir ./app/controllers</code>) and add a <code>TapController.rb</code> file inside. Let&#39;s start to define our controller like so:</p>
+<div class="highlight"><pre class='codehilite'><code class="ruby syntax"><span class="k">class</span> <span class="nc">TapController</span> <span class="o">&lt;</span> <span class="no">UIViewController</span>
+ <span class="k">def</span> <span class="nf">viewDidLoad</span>
+ <span class="k">super</span>
+
+ <span class="nb">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="no">UIColor</span><span class="o">.</span><span class="n">redColor</span>
+ <span class="k">end</span><