Skip to content

Instantly share code, notes, and snippets.

@ialexi
Created September 10, 2010 03:26
Show Gist options
  • Save ialexi/573033 to your computer and use it in GitHub Desktop.
Save ialexi/573033 to your computer and use it in GitHub Desktop.
Hedwig.GUIDE_CONTENT = {
"title": "Touch Application Guide",
"sections": [{
"title": "Introduction",
"articles": [{
"content": "<h1>A Brief Touch</h1>nn<p>It is very possible to build <em>awesome</em> touch-enabled applications in SproutCore.</p>nn<p>But, what makes an awesome touch-enabled application? Sure, it must accept touches,nbut with SproutCore's (constantly growing) touch support, this is now pretty easy:nmany existing interfaces, if built with newer SproutCore varieties, will function fine (or mostly fine),non both larger-screened touch devices (such as iPad) and the traditional desktop environment.</p>nn<p>But there are many differences between desktop and touch platforms:</p>nn<ul>n<li><strong>Precision.</strong> Touches are less precise than clicks. To compensate, controls should be larger.</li>n<li><strong>Performance.</strong> Touch-based devices tend to be slow (for now). To get around this just takes nsome elbow grease: there are many techniques to speed things up... many of which SproutCore willnhandle for you.</li>n<li><strong>Animation.</strong> Lack of animation looks okay on desktop (even if animation is cool)... but onntouch devices, non-animated interfaces look strange: touch is so much more realistic than mouse,nbut sudden changes without transitions are not realistic at all.</li>n<li><strong>Coolness.</strong> Touch-based interfaces are cool. That is all.</li>n</ul>nn<p>In this guide, we go over each of these, except for the last, which is rather vague; you'll have to figure outnyour own meaning of "coolness ".</p>",
"errors": [],
"demos": {},
"articleDirectory": "articles/touch/",
"outputDirectory": "build/",
"title": "A Brief Touch",
"any": "metadata",
"goes": "Here",
"damn": "gruber",
"this": "is still eye-readable",
"and": "He is wrong about touch apps."
},
{
"content": "<h1>Touch Events</h1>nn<p>SproutCore's touch events have a few great features:</p>nn<ul>n<li>Multiple views can receive touches simultaneously.</li>n<li>A single view can receive multiple touches.</li>n<li>A view can capture touches before allowing them to pass through to children.</li>n<li>Child views can release touches back to parent views that originally captured them.</li>n</ul>nn<p>We won't get into the last two in this article&emdash;they're quite sophisticated!</p>nn<h1>Simple Single-Touch Handling</h1>nn<p>You may be familiar with this SC.View method signature if you are familiar with SproutCore:</p>nn<pre><code class='syntax js'><span class="variable ">mouseDown</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>)n</code></pre>nn<p>With <code class='syntax js'><span class="variable ">mouseDown</span></code>, you can decide whether or not to accept the mouse event by returning eithern<code class='syntax js'><span class="class ">YES</span></code> or <code class='syntax js'><span class="class ">NO</span></code>.</p>nn<p>Touch events are similar, but work a bit differently:</p>nn<pre><code class='syntax js'><span class="variable ">touchStart</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>)n</code></pre>nn<p>Instead of being passed a raw event, <code class='syntax js'><span class="variable ">touchStart</span></code> is passed an <code class='syntax js'><span class="class ">SC</span>.<span class="class ">Touch</span></code> object.nIf you return YES from touchStart, your view will "own " the touch&emdash;in SproutCore terms, yournview will be the <em>touch responder</em>. For more information about what this entails, seenthe "Internals " article. Your view will own the touch until the touch ends.</p>nn<p><code class='syntax js'><span class="variable ">touchStart</span></code> will be called once for every touch that touches the view.</p>nn<p><strong>Note:</strong> By default, views only receive <code class='syntax js'><span class="variable ">touchStart</span></code> and touchEnd for a single touch. This isna feature intended to make it easier to handle such cases, very similar to Cocoa Touch's <code class='syntax js'><span class="variable ">acceptsMultitouch</span></code>nproperty&emdash;actually, SC.View uses the same property name! See the "Multitouch " article.</p>nn<h2>Anatomy of an SC.Touch</h2>nn<p>SC.Touch represents the touch from the time the user puts their finger on the screen until the time they lift it.</p>nn<p>The touch object acts like an event object in many ways. It has many other useful things, as well:</p>nn<ul>n<li><code class='syntax js'><span class="variable ">pageX</span></code> and <code class='syntax js'><span class="variable ">pageY</span></code> for the touch</li>n<li><code class='syntax js'><span class="variable ">event</span></code>: if in an event cycle, this contains the event. Otherwise, it is <code class='syntax js'><span class="variable ">undefined</span></code>. nYou will probably never need to access this.</li>n<li><code class='syntax js'><span class="variable ">preventDefault</span></code>: if the touch is connected with an event, this calls <code class='syntax js'><span class="variable ">preventDefault</span>()</code> on the event.</li>n<li><code class='syntax js'><span class="variable ">touchesForView</span>(<span class="variable ">view</span>)</code>: when supplied a view, will find all touches that the view is thentouch responder for. It is a CoreSet; to get an array, call <code class='syntax js'>.<span class="variable ">toArray</span></code> on the result.</li>n<li><code class='syntax js'><span class="variable ">averagedTouchesForView</span>(<span class="variable ">view</span>)</code>: When supplied a view, averages all the touches on that view,nreturning both an average position and an average distance from that position.</li>n</ul>nn<p><strong>Note:</strong> If you call <code class='syntax js'><span class="variable ">touchesForView</span>(<span class="this ">this</span>)</code> from <code class='syntax js'><span class="variable ">touchStart</span></code>, the touch supplied will not be in the setnreturned by <code class='syntax js'><span class="variable ">touchesForView</span>(<span class="this ">this</span>)</code>: you don't own the touch until you return YES.</p>nn<h2>touchEnd</h2>nn<p>Knowing when the touch starts is not very useful. At the very least, you will likely want to know when the touchnends as well.</p>nn<p>It is quite simple:</p>nn<pre><code class='syntax js'><span class="variable ">touchEnd</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>)n</code></pre>nn<p>It works exactly like <code class='syntax js'><span class="variable ">touchStart</span></code>. The touch will no longer be in the set of touches for the view,nso if you call <code class='syntax js'><span class="variable ">touch</span>.<span class="variable ">touchesForView</span>(<span class="this ">this</span>)</code>, you'll only receive any other active touches. If yournview does not accept multitouch, then the set is guaranteed to have no touches in it&emdash;you only receiven<code class='syntax js'><span class="variable ">touchEnd</span></code> for the last touch that ends.</p>nn<h2>Tracking Touches</h2>nn<p>Tracking touch movement is simple:</p>nn<pre><code class='syntax js'><span class="variable ">touchesDragged</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>, <span class="variable ">touches</span>)n</code></pre>nn<p>The <code class='syntax js'><span class="variable ">touches</span></code> argument is the set of touches on the view&emdash;the same set you get by calling <code class='syntax js'><span class="variable ">touchesForView</span>(<span class="this ">this</span>)</code>.nThis will have <em>all</em> touches, regardless of whether or not your view accepts multitouch.</p>nn<p>If your view does <em>not</em> accept multitouch, then it is even simpler:</p>nn<pre><code class='syntax js'><span class="variable ">x</span> = <span class="variable ">evt</span>.<span class="variable ">pageX</span>;n<span class="variable ">y</span> = <span class="variable ">evt</span>.<span class="variable ">pageY</span>;n</code></pre>nn<h2>Tip: Cross-Platform-iness</h2>nn<p>Did you realize that, assuming you don't use the set of view touches or other touch-specific API,nyou can do this:</p>nn<pre><code class='syntax js'><span class="variable ">mouseDown</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>) {n <span class="this ">this</span>.<span class="variable ">touchStart</span>(<span class="variable ">evt</span>);n},nn<span class="variable ">mouseDragged</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>) {n <span class="this ">this</span>.<span class="variable ">touchesDragged</span>(<span class="variable ">evt</span>);n},nn<span class="variable ">mouseUp</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>) {n <span class="this ">this</span>.<span class="variable ">touchEnd</span>(<span class="variable ">evt</span>);n}n</code></pre>nn<p>Of course, you can also redirect touch events to mouse events, but that is not as fun.</p>nn<h2>Putting it All Together</h2>nn<p>Here is a very simple demo that uses the methods described above to allow the user to move two viewsnaround the screen:</p>nn<p><a href='touch-demo.js' class='demo'>touch-demo.js</a></p>",
"errors": [],
"demos": {
"touch-demo.js": {
"ex": "var Dot = SC.View.extend({n touchStart: function(touch) {n var f = this.get("frame ");n this._touch = {n start: { x: touch.pageX, y: touch.pageY },n ourStart: { x: f.x, y: f.y, width: f.width, height: f.height }n };n return YES; // or we won't get touchesDraggedn },n n touchesDragged: function(evt, touches) {n var t = this._touch;n this.set("layout ", { n left: t.ourStart.x + evt.pageX - t.start.x,n top: t.ourStart.y + evt.pageY - t.start.y,n width: t.ourStart.width,n height: t.ourStart.heightn });n },n n touchEnd: function() {n // actually, we don't need to do anything here...n },n n // and now, redirect mouse events :)n mouseDown: function(evt) {n this.touchStart(evt);n },n n mouseDragged: function(evt) {n this.touchesDragged(evt);n },n n mouseUp: function(evt) {n this.touchEnd(evt);n }n});nvar MyExampleView = SC.View.extend({n backgroundColor: "white ",n childViews: "dot1 dot2 ".w(),n dot1: Dot.design({n backgroundColor: "red ",n layout: { left: 10, top: 10, width: 100, height: 100 }n }),n dot2: Dot.design({n backgroundColor: "blue ",n layout: { right: 10, bottom: 10, width: 100, height: 100 }n })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n",
"highlighted": "<span class="keyword ">var</span> <span class="class ">Dot</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">touchStart</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="keyword ">var</span> <span class="variable ">f</span> = <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;frame&quot;</span>);n <span class="this ">this</span>.<span class="variable ">_touch</span> = {n <span class="variable ">start</span>: { <span class="variable ">x</span>: <span class="variable ">touch</span>.<span class="variable ">pageX</span>, <span class="variable ">y</span>: <span class="variable ">touch</span>.<span class="variable ">pageY</span> },n <span class="variable ">ourStart</span>: { <span class="variable ">x</span>: <span class="variable ">f</span>.<span class="variable ">x</span>, <span class="variable ">y</span>: <span class="variable ">f</span>.<span class="variable ">y</span>, <span class="variable ">width</span>: <span class="variable ">f</span>.<span class="variable ">width</span>, <span class="variable ">height</span>: <span class="variable ">f</span>.<span class="variable ">height</span> }n };n <span class="keyword ">return</span> <span class="class ">YES</span>; <span class="comment ">// or we won't get touchesDragged</span>n },n n <span class="variable ">touchesDragged</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>, <span class="variable ">touches</span>) {n <span class="keyword ">var</span> <span class="variable ">t</span> = <span class="this ">this</span>.<span class="variable ">_touch</span>;n <span class="this ">this</span>.<span class="variable ">set</span>(<span class="string ">&quot;layout&quot;</span>, { n <span class="variable ">left</span>: <span class="variable ">t</span>.<span class="variable ">ourStart</span>.<span class="variable ">x</span> + <span class="variable ">evt</span>.<span class="variable ">pageX</span> - <span class="variable ">t</span>.<span class="variable ">start</span>.<span class="variable ">x</span>,n <span class="variable ">top</span>: <span class="variable ">t</span>.<span class="variable ">ourStart</span>.<span class="variable ">y</span> + <span class="variable ">evt</span>.<span class="variable ">pageY</span> - <span class="variable ">t</span>.<span class="variable ">start</span>.<span class="variable ">y</span>,n <span class="variable ">width</span>: <span class="variable ">t</span>.<span class="variable ">ourStart</span>.<span class="variable ">width</span>,n <span class="variable ">height</span>: <span class="variable ">t</span>.<span class="variable ">ourStart</span>.<span class="variable ">height</span>n });n },n n <span class="variable ">touchEnd</span>: <span class="keyword ">function</span>() {n <span class="comment ">// actually, we don't need to do anything here...</span>n },n n <span class="comment ">// and now, redirect mouse events :)</span>n <span class="variable ">mouseDown</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>) {n <span class="this ">this</span>.<span class="variable ">touchStart</span>(<span class="variable ">evt</span>);n },n n <span class="variable ">mouseDragged</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>) {n <span class="this ">this</span>.<span class="variable ">touchesDragged</span>(<span class="variable ">evt</span>);n },n n <span class="variable ">mouseUp</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>) {n <span class="this ">this</span>.<span class="variable ">touchEnd</span>(<span class="variable ">evt</span>);n }n});n<span class="keyword ">var</span> <span class="class ">MyExampleView</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;white&quot;</span>,n <span class="variable ">childViews</span>: <span class="string ">&quot;dot1 dot2&quot;</span>.<span class="variable ">w</span>(),n <span class="variable ">dot1</span>: <span class="class ">Dot</span>.<span class="variable ">design</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;red&quot;</span>,n <span class="variable ">layout</span>: { <span class="variable ">left</span>: <span class="number integer ">10</span>, <span class="variable ">top</span>: <span class="number integer ">10</span>, <span class="variable ">width</span>: <span class="number integer ">100</span>, <span class="variable ">height</span>: <span class="number integer ">100</span> }n }),n <span class="variable ">dot2</span>: <span class="class ">Dot</span>.<span class="variable ">design</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;blue&quot;</span>,n <span class="variable ">layout</span>: { <span class="variable ">right</span>: <span class="number integer ">10</span>, <span class="variable ">bottom</span>: <span class="number integer ">10</span>, <span class="variable ">width</span>: <span class="number integer ">100</span>, <span class="variable ">height</span>: <span class="number integer ">100</span> }n })n});nn<span class="comment ">// bootstrap code :)</span>n<span class="variable ">exports</span>.<span class="variable ">getDemoView</span> = <span class="keyword ">function</span>() {n <span class="keyword ">return</span> <span class="class ">MyExampleView</span>;n};n",
"original": "var Dot = SC.View.extend({n touchStart: function(touch) {n var f = this.get("frame ");n this._touch = {n start: { x: touch.pageX, y: touch.pageY },n ourStart: { x: f.x, y: f.y, width: f.width, height: f.height }n };n return YES; // or we won't get touchesDraggedn },n n touchesDragged: function(evt, touches) {n var t = this._touch;n this.set("layout ", { n left: t.ourStart.x + evt.pageX - t.start.x,n top: t.ourStart.y + evt.pageY - t.start.y,n width: t.ourStart.width,n height: t.ourStart.heightn });n },n n touchEnd: function() {n // actually, we don't need to do anything here...n },n n // and now, redirect mouse events :)n mouseDown: function(evt) {n this.touchStart(evt);n },n n mouseDragged: function(evt) {n this.touchesDragged(evt);n },n n mouseUp: function(evt) {n this.touchEnd(evt);n }n});nvar MyExampleView = SC.View.extend({n backgroundColor: "white ",n childViews: "dot1 dot2 ".w(),n dot1: Dot.design({n backgroundColor: "red ",n layout: { left: 10, top: 10, width: 100, height: 100 }n }),n dot2: Dot.design({n backgroundColor: "blue ",n layout: { right: 10, bottom: 10, width: 100, height: 100 }n })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n"
}
},
"articleDirectory": "articles/touch/",
"outputDirectory": "build/",
"title": "Touch Events"
},
{
"content": "<h1>Multitouch</h1>nn<p>Handling single touches is pretty easy&emdash;not that much different from handlingnmouse events. But what about multiple touches?</p>nn<h2>Accepting Multiple Touches</h2>nn<p>First, you have to tell your view that you do, indeed want to receive multiplentouches. By default, views only receive single touches. This is because is easier nto think in a single-touch model, and most controls only need to track a single touch.</p>nn<p>To accept multiple touches, just set the view's <code class='syntax js'><span class="variable ">acceptsMultitouch</span></code> property ton<code class='syntax js'><span class="class ">YES</span></code>.</p>nn<pre><code class='syntax js'><span class="variable ">view</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">acceptsMultitouch</span>: <span class="class ">YES</span>n});n</code></pre>nn<h2>Processing the Individual Touches</h2>nn<p>Even without the supplied helper function, processing individual touches is relativelynsimple:</p>nn<ul>n<li>You get a separate <code class='syntax js'><span class="variable ">touchStart</span></code> for each individual touch.</li>n<li>You get a separate <code class='syntax js'><span class="variable ">touchEnd</span></code> for each individual touch.</li>n<li>You get one <code class='syntax js'><span class="variable ">touchesDragged</span></code> each event cycle for all of your touches put together.</li>n</ul>nn<p>So, detecting individual touches starting and ending is simple. Detecting those touchesnmoving is not quite as simple, but still relatively easy.</p>nn<p>Remember how <code class='syntax js'><span class="variable ">touchesDragged</span></code> works:</p>nn<pre><code class='syntax js'><span class="variable ">touchesDragged</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>, <span class="variable ">touches</span>)n</code></pre>nn<p><code class='syntax js'><span class="variable ">touches</span></code> is an SC.CoreSet of SC.Touch objects. What can you do with a CoreSet? nYou can do a couple of things:</p>nn<ul>n<li>Turn it into an array and do whatever.</li>n<li>Call .forEach to iterate.</li>n</ul>nn<p>But you don't have to use the touches set at all. The <code class='syntax js'><span class="variable ">evt</span></code> has some useful properties and methods,ntoo:</p>nn<ul>n<li>pageX/pageY: the position of the first touch.</li>n<li>averagedTouchesForView: a method which returns the averaged touch positionnand the average distance of the touches from that position.</li>n</ul>nn<p><code class='syntax js'><span class="class ">SC</span>.<span class="class ">ScrollView</span></code>, for instance, makes heavy use of <code class='syntax js'><span class="variable ">averagedTouchesForView</span></code>, and neverndirectly touches the <code class='syntax js'><span class="variable ">touches</span></code> set.</p>nn<h2>Averaging Touches</h2>nn<p>It is often <em>very</em> useful to average the touches. </p>nn<p><code class='syntax js'><span class="variable ">averagedTouchesForView</span></code> returns an object with four properties:</p>nn<ul>n<li><strong><code class='syntax js'><span class="variable ">x</span></code></strong>: The average X position of the touch.</li>n<li><strong><code class='syntax js'><span class="variable ">y</span></code></strong>: The average Y position of the touch.</li>n<li><strong><code class='syntax js'><span class="variable ">d</span></code></strong>: average distance of the all touches from the average x/y position.</li>n<li><strong><code class='syntax js'><span class="variable ">touchCount</span></code></strong> The number of touches averaged.</li>n</ul>nn<p>You can call <code class='syntax js'><span class="variable ">averagedTouchesForView</span></code> on two separate objects: an <code class='syntax js'><span class="class ">SC</span>.<span class="class ">Event</span></code> object,nor an <code class='syntax js'><span class="class ">SC</span>.<span class="class ">Touch</span></code> object.</p>nn<p>The two work identically but for one important difference: when you call it on <code class='syntax js'><span class="class ">SC</span>.<span class="class ">Touch</span></code>,nyou <em>have the option</em> of telling the touch to add itself to the averaged set. Doing so makes no sensenin most cases: the touch would just be counted twice! But what about <code class='syntax js'><span class="variable ">touchStart</span></code>?</p>nn<p>Recall that during <code class='syntax js'><span class="variable ">touchStart</span></code>, the view does not yet own the touch. So, <code class='syntax js'><span class="variable ">averagedTouchesForView</span></code>nwould not, by default count it.</p>nn<pre><code class='syntax js'><span class="comment ">// on an event:</span>n<span class="keyword ">var</span> <span class="variable ">a</span> = <span class="variable ">evt</span>.<span class="variable ">averagedTouchesForView</span>(<span class="this ">this</span>);nn<span class="comment ">// on a touch</span>n<span class="keyword ">var</span> <span class="variable ">a</span> = <span class="variable ">touch</span>.<span class="variable ">averagedTouchesForView</span>(<span class="this ">this</span>);nn<span class="comment ">// on a touch, counting the touch itself</span>n<span class="keyword ">var</span> <span class="variable ">a</span> = <span class="variable ">touch</span>.<span class="variable ">averagedTouchesForView</span>(<span class="this ">this</span>, <span class="class ">YES</span>);n</code></pre>nn<h2>Thinking it Over</h2>nn<p>How might you use all of these to produce a good result?</p>nn<p>Let's take a simple example: moving and resizing something:</p>nn<p><a href='multitouch.js' class='demo'>multitouch.js</a></p>",
"errors": [],
"demos": {
"multitouch.js": {
"ex": "var Box = SC.View.extend({n _scale: 1,n _translateX: 0,n _translateY: 0,n acceptsMultitouch: YES,n touchStart: function(touch) {n this.recomputeTouchStatus(touch, YES);n return YES;n },n n touchesDragged: function(evt, touches) {n var t = this._touch;n var avg = evt.averagedTouchesForView(this);n n // translation is easy:n this._translateX = t.ourStart.x + avg.x - t.start.x;n this._translateY = t.ourStart.y + avg.y - t.start.y;n n // mathematically speaking, scale *= the end distance / the start distancen if (t.start.d > 1) { // but prevent divide-by-0n this._scale = t.ourStart.scale * (avg.d / t.start.d);n }n n // repositionn this._reposition();n },n n touchEnd: function(touch) {n this.recomputeTouchStatus(touch, NO);n },n n /**n With this, we recompute our touch status--updating the start positioning and scale.n */n recomputeTouchStatus: function(touch, considerTouch) {n var avg = touch.averagedTouchesForView(this, considerTouch);n this._touch = {n start: { x: avg.x, y: avg.y, d: avg.d },n ourStart: { x: this._translateX, y: this._translateY, scale: this._scale }n };n },n n /**n Repositions the view.n */n _reposition: function() {n this.get("layer ").style.webkitTransform = n "translate3d(" + this._translateX + "px, " + this._translateY + "px, 0px)" +n "scale3d(" + this._scale + ", " + this._scale + ", 1)";n console.error(this._translateX + "" + this._translateY + "" + this._scale);n },n n // and now, redirect mouse events :)n mouseDown: function(evt) {n this.touchStart(evt);n },n n mouseDragged: function(evt) {n this.touchesDragged(evt);n },n n mouseUp: function(evt) {n this.touchEnd(evt);n }n});nvar MyExampleView = SC.View.extend({n backgroundColor: "white ",n childViews: "box ".w(),n box: Box.design({n backgroundColor: "red ",n layout: { left: 10, top: 10, width: 200, height: 200 }n })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n",
"highlighted": "<span class="keyword ">var</span> <span class="class ">Box</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">_scale</span>: <span class="number integer ">1</span>,n <span class="variable ">_translateX</span>: <span class="number integer ">0</span>,n <span class="variable ">_translateY</span>: <span class="number integer ">0</span>,n <span class="variable ">acceptsMultitouch</span>: <span class="class ">YES</span>,n <span class="variable ">touchStart</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">recomputeTouchStatus</span>(<span class="variable ">touch</span>, <span class="class ">YES</span>);n <span class="keyword ">return</span> <span class="class ">YES</span>;n },n n <span class="variable ">touchesDragged</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>, <span class="variable ">touches</span>) {n <span class="keyword ">var</span> <span class="variable ">t</span> = <span class="this ">this</span>.<span class="variable ">_touch</span>;n <span class="keyword ">var</span> <span class="variable ">avg</span> = <span class="variable ">evt</span>.<span class="variable ">averagedTouchesForView</span>(<span class="this ">this</span>);n n <span class="comment ">// translation is easy:</span>n <span class="this ">this</span>.<span class="variable ">_translateX</span> = <span class="variable ">t</span>.<span class="variable ">ourStart</span>.<span class="variable ">x</span> + <span class="variable ">avg</span>.<span class="variable ">x</span> - <span class="variable ">t</span>.<span class="variable ">start</span>.<span class="variable ">x</span>;n <span class="this ">this</span>.<span class="variable ">_translateY</span> = <span class="variable ">t</span>.<span class="variable ">ourStart</span>.<span class="variable ">y</span> + <span class="variable ">avg</span>.<span class="variable ">y</span> - <span class="variable ">t</span>.<span class="variable ">start</span>.<span class="variable ">y</span>;n n <span class="comment ">// mathematically speaking, scale *= the end distance / the start distance</span>n <span class="keyword ">if</span> (<span class="variable ">t</span>.<span class="variable ">start</span>.<span class="variable ">d</span> &gt; <span class="number integer ">1</span>) { <span class="comment ">// but prevent divide-by-0</span>n <span class="this ">this</span>.<span class="variable ">_scale</span> = <span class="variable ">t</span>.<span class="variable ">ourStart</span>.<span class="variable ">scale</span> * (<span class="variable ">avg</span>.<span class="variable ">d</span> / <span class="variable ">t</span>.<span class="variable ">start</span>.<span class="variable ">d</span>);n }n n <span class="comment ">// reposition</span>n <span class="this ">this</span>.<span class="variable ">_reposition</span>();n },n n <span class="variable ">touchEnd</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">recomputeTouchStatus</span>(<span class="variable ">touch</span>, <span class="class ">NO</span>);n },n n <span class="multiline comment ">/**n With this, we recompute our touch status--updating the start positioning and scale.n */</span>n <span class="variable ">recomputeTouchStatus</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>, <span class="variable ">considerTouch</span>) {n <span class="keyword ">var</span> <span class="variable ">avg</span> = <span class="variable ">touch</span>.<span class="variable ">averagedTouchesForView</span>(<span class="this ">this</span>, <span class="variable ">considerTouch</span>);n <span class="this ">this</span>.<span class="variable ">_touch</span> = {n <span class="variable ">start</span>: { <span class="variable ">x</span>: <span class="variable ">avg</span>.<span class="variable ">x</span>, <span class="variable ">y</span>: <span class="variable ">avg</span>.<span class="variable ">y</span>, <span class="variable ">d</span>: <span class="variable ">avg</span>.<span class="variable ">d</span> },n <span class="variable ">ourStart</span>: { <span class="variable ">x</span>: <span class="this ">this</span>.<span class="variable ">_translateX</span>, <span class="variable ">y</span>: <span class="this ">this</span>.<span class="variable ">_translateY</span>, <span class="variable ">scale</span>: <span class="this ">this</span>.<span class="variable ">_scale</span> }n };n },n n <span class="multiline comment ">/**n Repositions the view.n */</span>n <span class="variable ">_reposition</span>: <span class="keyword ">function</span>() {n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">webkitTransform</span> = n <span class="string ">&quot;translate3d(&quot;</span> + <span class="this ">this</span>.<span class="variable ">_translateX</span> + <span class="string ">&quot;px,&quot;</span> + <span class="this ">this</span>.<span class="variable ">_translateY</span> + <span class="string ">&quot;px, 0px) &quot;</span> +n <span class="string ">&quot;scale3d(&quot;</span> + <span class="this ">this</span>.<span class="variable ">_scale</span> + <span class="string ">&quot;,&quot;</span> + <span class="this ">this</span>.<span class="variable ">_scale</span> + <span class="string ">&quot;,1)&quot;</span>;n <span class="variable ">console</span>.<span class="variable ">error</span>(<span class="this ">this</span>.<span class="variable ">_translateX</span> + <span class="string ">&quot; &quot;</span> + <span class="this ">this</span>.<span class="variable ">_translateY</span> + <span class="string ">&quot; &quot;</span> + <span class="this ">this</span>.<span class="variable ">_scale</span>);n },n n <span class="comment ">// and now, redirect mouse events :)</span>n <span class="variable ">mouseDown</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>) {n <span class="this ">this</span>.<span class="variable ">touchStart</span>(<span class="variable ">evt</span>);n },n n <span class="variable ">mouseDragged</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>) {n <span class="this ">this</span>.<span class="variable ">touchesDragged</span>(<span class="variable ">evt</span>);n },n n <span class="variable ">mouseUp</span>: <span class="keyword ">function</span>(<span class="variable ">evt</span>) {n <span class="this ">this</span>.<span class="variable ">touchEnd</span>(<span class="variable ">evt</span>);n }n});n<span class="keyword ">var</span> <span class="class ">MyExampleView</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;white&quot;</span>,n <span class="variable ">childViews</span>: <span class="string ">&quot;box&quot;</span>.<span class="variable ">w</span>(),n <span class="variable ">box</span>: <span class="class ">Box</span>.<span class="variable ">design</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;red&quot;</span>,n <span class="variable ">layout</span>: { <span class="variable ">left</span>: <span class="number integer ">10</span>, <span class="variable ">top</span>: <span class="number integer ">10</span>, <span class="variable ">width</span>: <span class="number integer ">200</span>, <span class="variable ">height</span>: <span class="number integer ">200</span> }n })n});nn<span class="comment ">// bootstrap code :)</span>n<span class="variable ">exports</span>.<span class="variable ">getDemoView</span> = <span class="keyword ">function</span>() {n <span class="keyword ">return</span> <span class="class ">MyExampleView</span>;n};n",
"original": "var Box = SC.View.extend({n _scale: 1,n _translateX: 0,n _translateY: 0,n acceptsMultitouch: YES,n touchStart: function(touch) {n this.recomputeTouchStatus(touch, YES);n return YES;n },n n touchesDragged: function(evt, touches) {n var t = this._touch;n var avg = evt.averagedTouchesForView(this);n n // translation is easy:n this._translateX = t.ourStart.x + avg.x - t.start.x;n this._translateY = t.ourStart.y + avg.y - t.start.y;n n // mathematically speaking, scale *= the end distance / the start distancen if (t.start.d > 1) { // but prevent divide-by-0n this._scale = t.ourStart.scale * (avg.d / t.start.d);n }n n // repositionn this._reposition();n },n n touchEnd: function(touch) {n this.recomputeTouchStatus(touch, NO);n },n n /**n With this, we recompute our touch status--updating the start positioning and scale.n */n recomputeTouchStatus: function(touch, considerTouch) {n var avg = touch.averagedTouchesForView(this, considerTouch);n this._touch = {n start: { x: avg.x, y: avg.y, d: avg.d },n ourStart: { x: this._translateX, y: this._translateY, scale: this._scale }n };n },n n /**n Repositions the view.n */n _reposition: function() {n this.get("layer ").style.webkitTransform = n "translate3d(" + this._translateX + "px, " + this._translateY + "px, 0px)" +n "scale3d(" + this._scale + ", " + this._scale + ", 1)";n console.error(this._translateX + "" + this._translateY + "" + this._scale);n },n n // and now, redirect mouse events :)n mouseDown: function(evt) {n this.touchStart(evt);n },n n mouseDragged: function(evt) {n this.touchesDragged(evt);n },n n mouseUp: function(evt) {n this.touchEnd(evt);n }n});nvar MyExampleView = SC.View.extend({n backgroundColor: "white ",n childViews: "box ".w(),n box: Box.design({n backgroundColor: "red ",n layout: { left: 10, top: 10, width: 200, height: 200 }n })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n"
}
},
"articleDirectory": "articles/touch/",
"outputDirectory": "build/",
"title": "Multitouch"
}]
},
{
"title": "Built-In Support",
"articles": [{
"content": "<h1>ButtonView Touch Support</h1>nn<p>ButtonView currently supports only a single touch (any other touches will be ignored). nThis is fine: why would you want to consider two touches in a button press?</p>nn<p>However, it does not simply relay the touches to <code class='syntax js'><span class="variable ">mouseDown</span></code> and <code class='syntax js'><span class="variable ">mouseUp</span></code>.</p>nn<p>Instead, it calculates the distance of the main touch (<code class='syntax js'><span class="variable ">evt</span></code>'s <code class='syntax js'><span class="variable ">pageX</span></code> and <code class='syntax js'><span class="variable ">pageY</span></code>)nfrom the button itself. This makes it easier to for a user to press a button while, for instance,nmoving their finger.</p>nn<p>This is all done for you automatically. Here's a simple demo:n<a href='touch-button.js' class='demo'>touch-button.js</a></p>nn<h2>Ace 2.0</h2>nn<p>Ace 2.0 supports several button sizes. In addition, it is capable of <em>automatically picking</em>na size based on the size of your control. And best of all, it <em>centers</em> the button imagenvertically in the space you allocate. This allows you to, for instance, make a button 44pxntall, but have the image only be 30px.</p>nn<p>You may use these options for <code class='syntax js'><span class="variable ">controlSize</span></code>:</p>nn<ul>n<li>SC.SMALL_CONTROL_SIZE. 18px.</li>n<li>SC.REGULAR_CONTROL_SIZE. 24px.</li>n<li>SC.HUGE_CONTROL_SIZE. 30px.</li>n<li>SC.JUMBO_CONTROL_SIZE. 44px.</li>n<li>SC.AUTO_CONTROL_SIZE. Automatically figures a control size (largest that will fit), butnthrows a warning if you don't have "height " set and it has to calculate the size.</li>n<li>SC.CALCULATED_CONTROL_SIZE. Like AUTO_CONTROL_SIZE, but calculates it.</li>n</ul>nn<p><a href='button-size.js' class='demo'>button-size.js</a></p>",
"errors": [],
"demos": {
"touch-button.js": {
"ex": "var MyExampleView = SC.View.extend({n backgroundColor: "white ",n childViews: "button ".w(),n button: SC.ButtonView.design({n layout: { centerX: 0, centerY: 0, width: 200, height: 44 },n title: "Tap Here: )",n controlSize: SC.AUTO_CONTROL_SIZEn })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n",
"highlighted": "<span class="keyword ">var</span> <span class="class ">MyExampleView</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;white&quot;</span>,n <span class="variable ">childViews</span>: <span class="string ">&quot;button&quot;</span>.<span class="variable ">w</span>(),n <span class="variable ">button</span>: <span class="class ">SC</span>.<span class="class ">ButtonView</span>.<span class="variable ">design</span>({n <span class="variable ">layout</span>: { <span class="variable ">centerX</span>: <span class="number integer ">0</span>, <span class="variable ">centerY</span>: <span class="number integer ">0</span>, <span class="variable ">width</span>: <span class="number integer ">200</span>, <span class="variable ">height</span>: <span class="number integer ">44</span> },n <span class="variable ">title</span>: <span class="string ">&quot;Tap Here :)&quot;</span>,n <span class="variable ">controlSize</span>: <span class="class ">SC</span>.<span class="class ">AUTO_CONTROL_SIZE</span>n })n});nn<span class="comment ">// bootstrap code :)</span>n<span class="variable ">exports</span>.<span class="variable ">getDemoView</span> = <span class="keyword ">function</span>() {n <span class="keyword ">return</span> <span class="class ">MyExampleView</span>;n};n",
"original": "var MyExampleView = SC.View.extend({n backgroundColor: "white ",n childViews: "button ".w(),n button: SC.ButtonView.design({n layout: { centerX: 0, centerY: 0, width: 200, height: 44 },n title: "Tap Here: )",n controlSize: SC.AUTO_CONTROL_SIZEn })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n"
},
"button-size.js": {
"ex": "var MyExampleView = SC.View.extend({n backgroundColor: "white ",n childViews: "form ".w(),n n form: SC.FormView.design({n layout: {centerX: 0, centerY: 0, width: 400, height: 300 },n childViews: "small regular huge jumbo ".w(),n small: SC.FormView.row(SC.ButtonView.design({n layout: { width: 100, height: 18 },n title: "I 'm TINY!",n controlSize: SC.SMALL_CONTROL_SIZEn })),n n regular: SC.FormView.row(SC.ButtonView.design({n layout: { width: 100, height: 24 },n title: "I'm Normal ! ",n controlSize: SC.REGULAR_CONTROL_SIZEn })),n n huge: SC.FormView.row(SC.ButtonView.design({n layout: { width: 100, height: 30 },n title: "I 'm huge.",n controlSize: SC.HUGE_CONTROL_SIZEn })),n n jumbo: SC.FormView.row(SC.ButtonView.design({n layout: { width: 150, height: 44 },n title: "i'm jumbo.",n controlSize: SC.JUMBO_CONTROL_SIZEn }))n n n })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n",
"highlighted": "<span class="keyword ">var</span> <span class="class ">MyExampleView</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;white&quot;</span>,n <span class="variable ">childViews</span>: <span class="string ">&quot;form&quot;</span>.<span class="variable ">w</span>(),n n <span class="variable ">form</span>: <span class="class ">SC</span>.<span class="class ">FormView</span>.<span class="variable ">design</span>({n <span class="variable ">layout</span>: {<span class="variable ">centerX</span>: <span class="number integer ">0</span>, <span class="variable ">centerY</span>: <span class="number integer ">0</span>, <span class="variable ">width</span>: <span class="number integer ">400</span>, <span class="variable ">height</span>: <span class="number integer ">300</span> },n <span class="variable ">childViews</span>: <span class="string ">&quot;small regular huge jumbo&quot;</span>.<span class="variable ">w</span>(),n <span class="variable ">small</span>: <span class="class ">SC</span>.<span class="class ">FormView</span>.<span class="variable ">row</span>(<span class="class ">SC</span>.<span class="class ">ButtonView</span>.<span class="variable ">design</span>({n <span class="variable ">layout</span>: { <span class="variable ">width</span>: <span class="number integer ">100</span>, <span class="variable ">height</span>: <span class="number integer ">18</span> },n <span class="variable ">title</span>: <span class="string ">&quot;I'm TINY!&quot;</span>,n <span class="variable ">controlSize</span>: <span class="class ">SC</span>.<span class="class ">SMALL_CONTROL_SIZE</span>n })),n n <span class="variable ">regular</span>: <span class="class ">SC</span>.<span class="class ">FormView</span>.<span class="variable ">row</span>(<span class="class ">SC</span>.<span class="class ">ButtonView</span>.<span class="variable ">design</span>({n <span class="variable ">layout</span>: { <span class="variable ">width</span>: <span class="number integer ">100</span>, <span class="variable ">height</span>: <span class="number integer ">24</span> },n <span class="variable ">title</span>: <span class="string ">&quot;I'm Normal!&quot;</span>,n <span class="variable ">controlSize</span>: <span class="class ">SC</span>.<span class="class ">REGULAR_CONTROL_SIZE</span>n })),n n <span class="variable ">huge</span>: <span class="class ">SC</span>.<span class="class ">FormView</span>.<span class="variable ">row</span>(<span class="class ">SC</span>.<span class="class ">ButtonView</span>.<span class="variable ">design</span>({n <span class="variable ">layout</span>: { <span class="variable ">width</span>: <span class="number integer ">100</span>, <span class="variable ">height</span>: <span class="number integer ">30</span> },n <span class="variable ">title</span>: <span class="string ">&quot;I'm huge.&quot;</span>,n <span class="variable ">controlSize</span>: <span class="class ">SC</span>.<span class="class ">HUGE_CONTROL_SIZE</span>n })),n n <span class="variable ">jumbo</span>: <span class="class ">SC</span>.<span class="class ">FormView</span>.<span class="variable ">row</span>(<span class="class ">SC</span>.<span class="class ">ButtonView</span>.<span class="variable ">design</span>({n <span class="variable ">layout</span>: { <span class="variable ">width</span>: <span class="number integer ">150</span>, <span class="variable ">height</span>: <span class="number integer ">44</span> },n <span class="variable ">title</span>: <span class="string ">&quot;i'm jumbo.&quot;</span>,n <span class="variable ">controlSize</span>: <span class="class ">SC</span>.<span class="class ">JUMBO_CONTROL_SIZE</span>n }))n n n })n});nn<span class="comment ">// bootstrap code :)</span>n<span class="variable ">exports</span>.<span class="variable ">getDemoView</span> = <span class="keyword ">function</span>() {n <span class="keyword ">return</span> <span class="class ">MyExampleView</span>;n};n",
"original": "var MyExampleView = SC.View.extend({n backgroundColor: "white ",n childViews: "form ".w(),n n form: SC.FormView.design({n layout: {centerX: 0, centerY: 0, width: 400, height: 300 },n childViews: "small regular huge jumbo ".w(),n small: SC.FormView.row(SC.ButtonView.design({n layout: { width: 100, height: 18 },n title: "I 'm TINY!",n controlSize: SC.SMALL_CONTROL_SIZEn })),n n regular: SC.FormView.row(SC.ButtonView.design({n layout: { width: 100, height: 24 },n title: "I'm Normal ! ",n controlSize: SC.REGULAR_CONTROL_SIZEn })),n n huge: SC.FormView.row(SC.ButtonView.design({n layout: { width: 100, height: 30 },n title: "I 'm huge.",n controlSize: SC.HUGE_CONTROL_SIZEn })),n n jumbo: SC.FormView.row(SC.ButtonView.design({n layout: { width: 150, height: 44 },n title: "i'm jumbo.",n controlSize: SC.JUMBO_CONTROL_SIZEn }))n n n })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n"
}
},
"articleDirectory": "articles/controls/button/",
"outputDirectory": "build/",
"title": "ButtonView Touch Support"
},
{
"content": "<h1>ScrollView Touch Support</h1>nn<p>SproutCore's ScrollView comes with built-in support for touch-basednscrolling, including momentum and bouncing. In addition, it has (somewhat experimental)nsupport for scaling.</p>nn<p>For many cases, just putting a view inside a ScrollView will "just work ". Still, you may wantnto set some settings.</p>nn<h2>Bouncing</h2>nn<p>By default, ScrollView will <em>always</em> bounce when scrolling vertically, regardless of thencontent's height, but only bounce horizontally <em>if</em> the content is wider than the ScrollView.nThis is controlled by two properties:</p>nn<ul>n<li>alwaysBounceHorizontal, which defaults to NO.</li>n<li>alwaysBounceVertical, which defaults to YES.</li>n</ul>nn<h2>Scaling</h2>nn<p>ScrollView has support for scaling, which you can use through a few properties:</p>nn<ul>n<li>canScale. Specifies whether the content may be scaled. If YES, using two fingersn(in that classic "pinch gesture ") will zoom the content.</li>n<li>minimumScale: The minimum scale value. Default: 0.25.</li>n<li>maximumScale: The maximum scale value. Default: 2.0.</li>n</ul>nn<p><a href='touch.js' class='demo'>touch.js</a></p>",
"errors": [],
"demos": {
"touch.js": {
"ex": "/*globals Hedwig*/nvar MyExampleView = SC.ScrollView.extend({n horizontalAlign: SC.ALIGN_CENTER,n verticalAlign: SC.ALIGN_MIDDLE,n backgroundColor: "#555 ",n n canScale: YES,n alwaysBounceVertical: NO,n n contentView: SC.ImageView.design({n layout: { left: 0, top: 0, width: 1357, height: 2048 },n value: Hedwig.SAMPLE_IMAGEn })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n",
"highlighted": "<span class="multiline comment ">/*globals Hedwig*/</span>n<span class="keyword ">var</span> <span class="class ">MyExampleView</span> = <span class="class ">SC</span>.<span class="class ">ScrollView</span>.<span class="variable ">extend</span>({n <span class="variable ">horizontalAlign</span>: <span class="class ">SC</span>.<span class="class ">ALIGN_CENTER</span>,n <span class="variable ">verticalAlign</span>: <span class="class ">SC</span>.<span class="class ">ALIGN_MIDDLE</span>,n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;#555&quot;</span>,n n <span class="variable ">canScale</span>: <span class="class ">YES</span>,n <span class="variable ">alwaysBounceVertical</span>: <span class="class ">NO</span>,n n <span class="variable ">contentView</span>: <span class="class ">SC</span>.<span class="class ">ImageView</span>.<span class="variable ">design</span>({n <span class="variable ">layout</span>: { <span class="variable ">left</span>: <span class="number integer ">0</span>, <span class="variable ">top</span>: <span class="number integer ">0</span>, <span class="variable ">width</span>: <span class="number integer ">1357</span>, <span class="variable ">height</span>: <span class="number integer ">2048</span> },n <span class="variable ">value</span>: <span class="class ">Hedwig</span>.<span class="class ">SAMPLE_IMAGE</span>n })n});nn<span class="comment ">// bootstrap code :)</span>n<span class="variable ">exports</span>.<span class="variable ">getDemoView</span> = <span class="keyword ">function</span>() {n <span class="keyword ">return</span> <span class="class ">MyExampleView</span>;n};n",
"original": "/*globals Hedwig*/nvar MyExampleView = SC.ScrollView.extend({n horizontalAlign: SC.ALIGN_CENTER,n verticalAlign: SC.ALIGN_MIDDLE,n backgroundColor: "#555 ",n n canScale: YES,n alwaysBounceVertical: NO,n n contentView: SC.ImageView.design({n layout: { left: 0, top: 0, width: 1357, height: 2048 },n value: Hedwig.SAMPLE_IMAGEn })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n"
}
},
"articleDirectory": "articles/controls/scroll/",
"outputDirectory": "build/",
"title": "ScrollView Touch Support"
}]
},
{
"title": "Advanced Concepts",
"articles": [{
"content": "<h1>Capturing</h1>nn<p>There are many instances where you may have to capture touches. We'll consider scroll views,nhowever, because it is very easy to explain <em>why</em> they need to be able to.</p>nn<p>If you are implementing a scroll view, you'll need to capture the touch before the target view gets it.</p>nn<p>This is because scroll views have some subtle quirks. For instance, a touch should not passnthrough to the actual target until a split-second has passed&emdash;this is to prevent flicker shouldnthe user decide to scroll rather than touch the target. Also, it needs to add itself to what isncalled the "touch responder stack " <em>before</em> the target view, so that the target view can hand control backnsimply as discussed above.</p>nn<h2>Touch Responder Stack</h2>nn<p>Very briefly mentioned in the "Touch Events " article, the <em>touch responder</em> is the view whichn"owns " a touch&emdash;the view which gets all touchesDragged events and the touchEnd for that touch.</p>nn<p>However, in our scroll view case, we want to pass control to the target view, but allow itnto <em>transfer control back</em> (a process covered in "Releasing "). That means it needs to knownwhat view to hand it back to. Also, what if you had a scroll view <em>within</em> a scroll view?</p>nn<p>To solve these issues, there is a "stack " of those views to which control can be passed back to.</p>nn<p>You generally work with it by either:</p>nn<ul>n<li>Specifically telling it to "stack " your view when capturing a touch.</li>n<li>Telling it to change touch responder to a touch responder in the stack (see Releasing)</li>n</ul>nn<h2>Capturing</h2>nn<p>Capturing the touch is simple. Before starting at the target view and working itsnway up to the root calling touchStart (the same way all SproutCore events work), nSproutCore's touch events go the opposite direction, starting at the root and working their way downnto the target, calling a method named captureTouch:</p>nn<pre><code class='syntax js'><span class="variable ">captureTouch</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n</code></pre>nn<p>If the view returns YES, the touchStart event will be sent directly to it.</p>nn<p>You could then use invokeLater to wait that split-second. But then what? You don't actuallynknow what the target view should be. What you need is to start at the original target, andndo the whole working up to it calling captureTouch and work down from it calling touchStartnthing&emdash;but this time, starting from your own view, rather than the root. </p>nn<p>Thankfully, you can do this in one line of code:</p>nn<pre><code class='syntax js'><span class="variable ">touch</span>.<span class="variable ">captureTouch</span>(<span class="this ">this</span>, <span class="class ">YES</span>); <span class="comment ">// the YES means stack, which I'm guessing you'd want to do</span>n<span class="comment ">// so that the new view can easily pass control back to this...</span>n<span class="comment ">// ... but you may know better than me.</span>n</code></pre>nn<p>What happens next depends on whether or not you told it to stack your view:</p>nn<ul>n<li>If stacked, you will receive a touchCancelled when the touch actually ends, unless thenview which captures the touch hands control back to your view. If it does, you will <em>not</em>nreceive another <code class='syntax js'><span class="variable ">touchStart</span></code>, but you <em>will</em> start receiving <code class='syntax js'><span class="variable ">touchesDragged</span></code> and nwill receive a <code class='syntax js'><span class="variable ">touchEnd</span></code> when the touch ends.</li>n<li>If not stacked, your view will receive <code class='syntax js'><span class="variable ">touchCancelled</span></code>.</li>n</ul>nn<h2>What Does It Look Like?</h2>nn<p>In this example, we have two boxes, each containing an inner box. The outer boxesncapture touches, and only pass them to the inner box after a delay. The top box stacks,nthe other one does not; this causes, as described above, a difference in when touchEnd/Cancellednis called on the outer boxes.</p>nn<p><a href='capturing.js' class='demo'>capturing.js</a></p>",
"errors": [],
"demos": {
"capturing.js": {
"ex": "/**n This is our test view. It will capture a touch, and, after one second,n pass it through to the child view. Depending on the shouldStack property,n it will stack while passing.n n In this example, the top view stacks, the bottom one does not.n*/nvar Tester = SC.View.extend({n backgroundColor: "white ",n n shouldStack: NO,n captureTouch: function() {n return YES;n },n n touchStart: function(touch) {n this._hasTouch = touch;n this.get("layer ").style.backgroundColor = "red ";n n // in one second, we'll pass the touch along.n this.invokeLater("beginContentTouches ", 1000, touch);n return YES;n },n n beginContentTouches: function(touch) {n // if our touch hasn't changed in the meantimen if (touch === this._hasTouch) {n // we'll pass the touch along.n touch.captureTouch(this, this.get("shouldStack "));n }n },n n touchEnd: function(touch) {n this._hasTouch = NO;n this.get("layer ").style.backgroundColor = "white ";n },n n touchCancelled: function(touch) {n this._hasTouch = NO;n this.get("layer ").style.backgroundColor = "white ";n },n n childViews: "inner ".w(),n inner: SC.View.design({n layout: { left: 50, top: 50, right: 50, bottom: 50 },n backgroundColor: "gray ",n touchStart: function() {n this.get("layer ").style.backgroundColor = "blue ";n return YES;n },n n touchEnd: function() {n this.get("layer ").style.backgroundColor = "gray ";n }n n })n n});nnvar MyExampleView = SC.View.extend({n backgroundColor: "#aaa ",n childViews: "stacks doesNot ".w(),n stacks: Tester.extend({n layout: { top: 10, left: 10, width: 200, height: 200 },n shouldStack: YESn }),n n doesNot: Tester.extend({n layout: { top: 230, left: 10, width: 200, height: 200 },n shouldStack: NO n })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n",
"highlighted": "<span class="multiline comment ">/**n This is our test view. It will capture a touch, and, after one second,n pass it through to the child view. Depending on the shouldStack property,n it will stack while passing.n n In this example, the top view stacks, the bottom one does not.n*/</span>n<span class="keyword ">var</span> <span class="class ">Tester</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;white&quot;</span>,n n <span class="variable ">shouldStack</span>: <span class="class ">NO</span>,n <span class="variable ">captureTouch</span>: <span class="keyword ">function</span>() {n <span class="keyword ">return</span> <span class="class ">YES</span>;n },n n <span class="variable ">touchStart</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">_hasTouch</span> = <span class="variable ">touch</span>;n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;red&quot;</span>;n n <span class="comment ">// in one second, we'll pass the touch along.</span>n <span class="this ">this</span>.<span class="variable ">invokeLater</span>(<span class="string ">&quot;beginContentTouches&quot;</span>, <span class="number integer ">1000</span>, <span class="variable ">touch</span>);n <span class="keyword ">return</span> <span class="class ">YES</span>;n },n n <span class="variable ">beginContentTouches</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="comment ">// if our touch hasn't changed in the meantime</span>n <span class="keyword ">if</span> (<span class="variable ">touch</span> === <span class="this ">this</span>.<span class="variable ">_hasTouch</span>) {n <span class="comment ">// we'll pass the touch along.</span>n <span class="variable ">touch</span>.<span class="variable ">captureTouch</span>(<span class="this ">this</span>, <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;shouldStack&quot;</span>));n }n },n n <span class="variable ">touchEnd</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">_hasTouch</span> = <span class="class ">NO</span>;n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;white&quot;</span>;n },n n <span class="variable ">touchCancelled</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">_hasTouch</span> = <span class="class ">NO</span>;n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;white&quot;</span>;n },n n <span class="variable ">childViews</span>: <span class="string ">&quot;inner&quot;</span>.<span class="variable ">w</span>(),n <span class="variable ">inner</span>: <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">design</span>({n <span class="variable ">layout</span>: { <span class="variable ">left</span>: <span class="number integer ">50</span>, <span class="variable ">top</span>: <span class="number integer ">50</span>, <span class="variable ">right</span>: <span class="number integer ">50</span>, <span class="variable ">bottom</span>: <span class="number integer ">50</span> },n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;gray&quot;</span>,n <span class="variable ">touchStart</span>: <span class="keyword ">function</span>() {n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;blue&quot;</span>;n <span class="keyword ">return</span> <span class="class ">YES</span>;n },n n <span class="variable ">touchEnd</span>: <span class="keyword ">function</span>() {n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;gray&quot;</span>;n }n n })n n});nn<span class="keyword ">var</span> <span class="class ">MyExampleView</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;#aaa&quot;</span>,n <span class="variable ">childViews</span>: <span class="string ">&quot;stacks doesNot&quot;</span>.<span class="variable ">w</span>(),n <span class="variable ">stacks</span>: <span class="class ">Tester</span>.<span class="variable ">extend</span>({n <span class="variable ">layout</span>: { <span class="variable ">top</span>: <span class="number integer ">10</span>, <span class="variable ">left</span>: <span class="number integer ">10</span>, <span class="variable ">width</span>: <span class="number integer ">200</span>, <span class="variable ">height</span>: <span class="number integer ">200</span> },n <span class="variable ">shouldStack</span>: <span class="class ">YES</span>n }),n n <span class="variable ">doesNot</span>: <span class="class ">Tester</span>.<span class="variable ">extend</span>({n <span class="variable ">layout</span>: { <span class="variable ">top</span>: <span class="number integer ">230</span>, <span class="variable ">left</span>: <span class="number integer ">10</span>, <span class="variable ">width</span>: <span class="number integer ">200</span>, <span class="variable ">height</span>: <span class="number integer ">200</span> },n <span class="variable ">shouldStack</span>: <span class="class ">NO</span> n })n});nn<span class="comment ">// bootstrap code :)</span>n<span class="variable ">exports</span>.<span class="variable ">getDemoView</span> = <span class="keyword ">function</span>() {n <span class="keyword ">return</span> <span class="class ">MyExampleView</span>;n};n",
"original": "/**n This is our test view. It will capture a touch, and, after one second,n pass it through to the child view. Depending on the shouldStack property,n it will stack while passing.n n In this example, the top view stacks, the bottom one does not.n*/nvar Tester = SC.View.extend({n backgroundColor: "white ",n n shouldStack: NO,n captureTouch: function() {n return YES;n },n n touchStart: function(touch) {n this._hasTouch = touch;n this.get("layer ").style.backgroundColor = "red ";n n // in one second, we'll pass the touch along.n this.invokeLater("beginContentTouches ", 1000, touch);n return YES;n },n n beginContentTouches: function(touch) {n // if our touch hasn't changed in the meantimen if (touch === this._hasTouch) {n // we'll pass the touch along.n touch.captureTouch(this, this.get("shouldStack "));n }n },n n touchEnd: function(touch) {n this._hasTouch = NO;n this.get("layer ").style.backgroundColor = "white ";n },n n touchCancelled: function(touch) {n this._hasTouch = NO;n this.get("layer ").style.backgroundColor = "white ";n },n n childViews: "inner ".w(),n inner: SC.View.design({n layout: { left: 50, top: 50, right: 50, bottom: 50 },n backgroundColor: "gray ",n touchStart: function() {n this.get("layer ").style.backgroundColor = "blue ";n return YES;n },n n touchEnd: function() {n this.get("layer ").style.backgroundColor = "gray ";n }n n })n n});nnvar MyExampleView = SC.View.extend({n backgroundColor: "#aaa ",n childViews: "stacks doesNot ".w(),n stacks: Tester.extend({n layout: { top: 10, left: 10, width: 200, height: 200 },n shouldStack: YESn }),n n doesNot: Tester.extend({n layout: { top: 230, left: 10, width: 200, height: 200 },n shouldStack: NO n })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n"
}
},
"articleDirectory": "articles/touch/",
"outputDirectory": "build/",
"title": "Capturing"
},
{
"content": "<h1>Releasing</h1>nn<p>Why would you want to give up a touch? Imagine that your control is inside a <code class='syntax js'><span class="class ">SC</span>.<span class="class ">ScrollView</span></code>:nif the touch moves too much, perhaps it should be considered a scroll, rather than annaction for your control.</p>nn<p>From touchesDragged, you would give up touch responder status through a line like this:</p>nn<pre><code class='syntax js'><span class="variable ">someTouch</span>.<span class="variable ">makeTouchResponder</span>(<span class="variable ">someTouch</span>.<span class="variable ">nextTouchResponder</span>);n</code></pre>nn<p>The touch's nextTouchResponder is the responder that is the <em>parent</em> touch responder; throughndevious trickery (see <em>Capturing Touches</em>), ScrollView receives touch responder status <em>before</em>nother views; further, it doesn't just hand touch responder status to the target view (your view)--nit adds the responder to a stack of touch responders for the touch, so the responders can easilynreturn to their parent responder (which is what you do with the above line of code.)</p>nn<p>Remember, though, that touchesDragged is called with a set of touches. It is really easynto change the responder for all of the touches simultaneously, should you wish to do so:</p>nn<pre><code class='syntax js'><span class="variable ">touches</span>.<span class="variable ">forEach</span>(<span class="keyword ">function</span>(<span class="variable ">touch</span>){n <span class="variable ">touch</span>.<span class="variable ">makeTouchResponder</span>(<span class="variable ">touch</span>.<span class="variable ">nextTouchResponder</span>);n});n</code></pre>nn<p>Perhaps you want to pass control only if the responder is scrollable:</p>nn<pre><code class='syntax js'><span class="keyword ">if</span> (<span class="variable ">touch</span>.<span class="variable ">nextTouchResponder</span> &amp;&amp; <span class="variable ">touch</span>.<span class="variable ">nextTouchResponder</span>.<span class="variable ">isScrollable</span>) {n <span class="variable ">touch</span>.<span class="variable ">makeTouchResponder</span>(<span class="variable ">touch</span>.<span class="variable ">nextTouchResponder</span>);n}n</code></pre>nn<p><code class='syntax js'><span class="variable ">touchCancelled</span></code> will be called on your view automatically.</p>nn<h2>What Does It Look Like?</h2>nn<p>In this example, there is a single white box, containing a gray inner box. When you pressnon the inner box, the outer box will capture the touch first. After a delay, it re-capturesnby calling captureTouch, and the inner view receives it. This is just like the "Capturing " demo.</p>nn<p>However, the inner view, after a second, will release it back.</p>nn<p><a href='releasing.js' class='demo'>releasing.js</a></p>",
"errors": [],
"demos": {
"releasing.js": {
"ex": "/**n This is similar to the "Capturing " example.n n In fact, the outer view is identical. Only the inner view has changed.n As we can't pass control back without stacking, this demo _only_ includesn the stacking method.n n The inner view, just like the outer view, passes control back after a specificn time period.n*/nvar Tester = SC.View.extend({n backgroundColor: "white ",n n captureTouch: function() {n return YES;n },n n touchStart: function(touch) {n this._hasTouch = touch;n this.get("layer ").style.backgroundColor = "red ";n n // in one second, we'll pass the touch along.n this.invokeLater("beginContentTouches ", 1000, touch);n return YES;n },n n beginContentTouches: function(touch) {n // if our touch hasn't changed in the meantimen if (touch === this._hasTouch) {n // we'll pass the touch along.n touch.captureTouch(this, YES);n }n },n n touchEnd: function(touch) {n this._hasTouch = NO;n this.get("layer ").style.backgroundColor = "white ";n },n n touchCancelled: function(touch) {n this._hasTouch = NO;n this.get("layer ").style.backgroundColor = "white ";n },n n childViews: "inner ".w(),n inner: SC.View.design({n layout: { left: 50, top: 50, right: 50, bottom: 50 },n backgroundColor: "gray ",n touchStart: function(touch) {n this._touch = touch;n this.get("layer ").style.backgroundColor = "blue ";n this.invokeLater("releaseTouch ", 1000, touch);n return YES;n },n n releaseTouch: function(touch) {n if (touch === this._touch) {n touch.makeTouchResponder(touch.nextTouchResponder);n }n },n n touchEnd: function(touch) {n this._touch = NO;n this.get("layer ").style.backgroundColor = "gray ";n },n n touchCancelled: function(touch) {n this._touch = NO;n this.get("layer ").style.backgroundColor = "gray ";n }n n })n n});nnvar MyExampleView = SC.View.extend({n backgroundColor: "#aaa ",n childViews: "demo ".w(),n demo: Tester.extend({n layout: { top: 10, left: 10, width: 200, height: 200 },n shouldStack: YESn })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n",
"highlighted": "<span class="multiline comment ">/**n This is similar to the &quot;Capturing&quot; example.n n In fact, the outer view is identical. Only the inner view has changed.n As we can't pass control back without stacking, this demo _only_ includesn the stacking method.n n The inner view, just like the outer view, passes control back after a specificn time period.n*/</span>n<span class="keyword ">var</span> <span class="class ">Tester</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;white&quot;</span>,n n <span class="variable ">captureTouch</span>: <span class="keyword ">function</span>() {n <span class="keyword ">return</span> <span class="class ">YES</span>;n },n n <span class="variable ">touchStart</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">_hasTouch</span> = <span class="variable ">touch</span>;n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;red&quot;</span>;n n <span class="comment ">// in one second, we'll pass the touch along.</span>n <span class="this ">this</span>.<span class="variable ">invokeLater</span>(<span class="string ">&quot;beginContentTouches&quot;</span>, <span class="number integer ">1000</span>, <span class="variable ">touch</span>);n <span class="keyword ">return</span> <span class="class ">YES</span>;n },n n <span class="variable ">beginContentTouches</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="comment ">// if our touch hasn't changed in the meantime</span>n <span class="keyword ">if</span> (<span class="variable ">touch</span> === <span class="this ">this</span>.<span class="variable ">_hasTouch</span>) {n <span class="comment ">// we'll pass the touch along.</span>n <span class="variable ">touch</span>.<span class="variable ">captureTouch</span>(<span class="this ">this</span>, <span class="class ">YES</span>);n }n },n n <span class="variable ">touchEnd</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">_hasTouch</span> = <span class="class ">NO</span>;n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;white&quot;</span>;n },n n <span class="variable ">touchCancelled</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">_hasTouch</span> = <span class="class ">NO</span>;n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;white&quot;</span>;n },n n <span class="variable ">childViews</span>: <span class="string ">&quot;inner&quot;</span>.<span class="variable ">w</span>(),n <span class="variable ">inner</span>: <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">design</span>({n <span class="variable ">layout</span>: { <span class="variable ">left</span>: <span class="number integer ">50</span>, <span class="variable ">top</span>: <span class="number integer ">50</span>, <span class="variable ">right</span>: <span class="number integer ">50</span>, <span class="variable ">bottom</span>: <span class="number integer ">50</span> },n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;gray&quot;</span>,n <span class="variable ">touchStart</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">_touch</span> = <span class="variable ">touch</span>;n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;blue&quot;</span>;n <span class="this ">this</span>.<span class="variable ">invokeLater</span>(<span class="string ">&quot;releaseTouch&quot;</span>, <span class="number integer ">1000</span>, <span class="variable ">touch</span>);n <span class="keyword ">return</span> <span class="class ">YES</span>;n },n n <span class="variable ">releaseTouch</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="keyword ">if</span> (<span class="variable ">touch</span> === <span class="this ">this</span>.<span class="variable ">_touch</span>) {n <span class="variable ">touch</span>.<span class="variable ">makeTouchResponder</span>(<span class="variable ">touch</span>.<span class="variable ">nextTouchResponder</span>);n }n },n n <span class="variable ">touchEnd</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">_touch</span> = <span class="class ">NO</span>;n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;gray&quot;</span>;n },n n <span class="variable ">touchCancelled</span>: <span class="keyword ">function</span>(<span class="variable ">touch</span>) {n <span class="this ">this</span>.<span class="variable ">_touch</span> = <span class="class ">NO</span>;n <span class="this ">this</span>.<span class="variable ">get</span>(<span class="string ">&quot;layer&quot;</span>).<span class="variable ">style</span>.<span class="variable ">backgroundColor</span> = <span class="string ">&quot;gray&quot;</span>;n }n n })n n});nn<span class="keyword ">var</span> <span class="class ">MyExampleView</span> = <span class="class ">SC</span>.<span class="class ">View</span>.<span class="variable ">extend</span>({n <span class="variable ">backgroundColor</span>: <span class="string ">&quot;#aaa&quot;</span>,n <span class="variable ">childViews</span>: <span class="string ">&quot;demo&quot;</span>.<span class="variable ">w</span>(),n <span class="variable ">demo</span>: <span class="class ">Tester</span>.<span class="variable ">extend</span>({n <span class="variable ">layout</span>: { <span class="variable ">top</span>: <span class="number integer ">10</span>, <span class="variable ">left</span>: <span class="number integer ">10</span>, <span class="variable ">width</span>: <span class="number integer ">200</span>, <span class="variable ">height</span>: <span class="number integer ">200</span> },n <span class="variable ">shouldStack</span>: <span class="class ">YES</span>n })n});nn<span class="comment ">// bootstrap code :)</span>n<span class="variable ">exports</span>.<span class="variable ">getDemoView</span> = <span class="keyword ">function</span>() {n <span class="keyword ">return</span> <span class="class ">MyExampleView</span>;n};n",
"original": "/**n This is similar to the "Capturing " example.n n In fact, the outer view is identical. Only the inner view has changed.n As we can't pass control back without stacking, this demo _only_ includesn the stacking method.n n The inner view, just like the outer view, passes control back after a specificn time period.n*/nvar Tester = SC.View.extend({n backgroundColor: "white ",n n captureTouch: function() {n return YES;n },n n touchStart: function(touch) {n this._hasTouch = touch;n this.get("layer ").style.backgroundColor = "red ";n n // in one second, we'll pass the touch along.n this.invokeLater("beginContentTouches ", 1000, touch);n return YES;n },n n beginContentTouches: function(touch) {n // if our touch hasn't changed in the meantimen if (touch === this._hasTouch) {n // we'll pass the touch along.n touch.captureTouch(this, YES);n }n },n n touchEnd: function(touch) {n this._hasTouch = NO;n this.get("layer ").style.backgroundColor = "white ";n },n n touchCancelled: function(touch) {n this._hasTouch = NO;n this.get("layer ").style.backgroundColor = "white ";n },n n childViews: "inner ".w(),n inner: SC.View.design({n layout: { left: 50, top: 50, right: 50, bottom: 50 },n backgroundColor: "gray ",n touchStart: function(touch) {n this._touch = touch;n this.get("layer ").style.backgroundColor = "blue ";n this.invokeLater("releaseTouch ", 1000, touch);n return YES;n },n n releaseTouch: function(touch) {n if (touch === this._touch) {n touch.makeTouchResponder(touch.nextTouchResponder);n }n },n n touchEnd: function(touch) {n this._touch = NO;n this.get("layer ").style.backgroundColor = "gray ";n },n n touchCancelled: function(touch) {n this._touch = NO;n this.get("layer ").style.backgroundColor = "gray ";n }n n })n n});nnvar MyExampleView = SC.View.extend({n backgroundColor: "#aaa ",n childViews: "demo ".w(),n demo: Tester.extend({n layout: { top: 10, left: 10, width: 200, height: 200 },n shouldStack: YESn })n});nn// bootstrap code :)nexports.getDemoView = function() {n return MyExampleView;n};n"
}
},
"articleDirectory": "articles/touch/",
"outputDirectory": "build/",
"title": "Releasing"
}]
}],
"file": "build/guides/touch"
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment