<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Roger Boesch &#187; OpenGL</title>
	<atom:link href="http://www.rogerboesch.com/tag/opengl/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.rogerboesch.com</link>
	<description>iPhone, iPad und iPod Touch</description>
	<lastBuildDate>Thu, 01 Apr 2010 20:04:49 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>CoverFlow</title>
		<link>http://www.rogerboesch.com/2009/08/16/coverflow-programmieren/</link>
		<comments>http://www.rogerboesch.com/2009/08/16/coverflow-programmieren/#comments</comments>
		<pubDate>Sun, 16 Aug 2009 16:05:40 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[iPhone/iPad Programming]]></category>
		<category><![CDATA[Cover Flow]]></category>
		<category><![CDATA[OpenGL]]></category>

		<guid isPermaLink="false">http://www.rogerboesch.com/?p=611</guid>
		<description><![CDATA[
Bereits Anfang des Jahres habe ich einen Blog Beitrag zum Thema Cover Flow geschrieben. Ein Thema, zu dem mich seither immer wieder Mails und Anfragen erreicht haben. Insbesondere für Entwickler, die noch nicht so lange mit Objective-C und Cocoa entwickeln ist das Thema zugegebenermassen auch nicht ganz ohne.
Möchte man einen Cover Flow in eine eigene [...]]]></description>
			<content:encoded><![CDATA[<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="344" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/zSsns24bUPo&amp;hl=en&amp;fs=1&amp;" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="425" height="344" src="http://www.youtube.com/v/zSsns24bUPo&amp;hl=en&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object><br />
Bereits Anfang des Jahres habe ich einen Blog Beitrag zum Thema Cover Flow geschrieben. Ein Thema, zu dem mich seither immer wieder Mails und Anfragen erreicht haben. Insbesondere für Entwickler, die noch nicht so lange mit Objective-C und Cocoa entwickeln ist das Thema zugegebenermassen auch nicht ganz ohne.<br />
Möchte man einen Cover Flow in eine eigene App einbauen gibt es zunächst drei Möglichkeiten:</p>
<ul>
<li> Man benutzt den Cover Flow View aus den Private Frameworks von Apple (Keine gute Idee, wenn man die App später auch erfolgreich in den AppStore bringen möchte)</li>
<li> Man benutzt Core Animation und Layers</li>
<li> Man verwendet OpenGL</li>
</ul>
<p>Auf die erste Möglichkeit gehe ich aus verständlichen Gründen nicht weiter ein. Die zweite Variante mit Core Animation kam in einem meiner recht frühen Projekte zum Einsatz (Den Beispielcode erläutere ich nacholgend) und hat sich gut bewährt. Variante 3 setze ich aktuell in den Projekten ein, kann als Library gekauft werden und ist zudem im Video zu sehen.</p>
<p>Da das Core Animation Framework auf dem iPhone nicht ganz so mächtig wie unter MacOSX ist, muss man auf Filter verzichten, trotzdem sind schöne Ergebnisse zu erzielen. Doch nun zum Code.</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #6e371a;">#import</span>
<span style="color: #6e371a;">#import </span>
&nbsp;
<span style="color: #a61390;">@protocol</span> RBCoverFlowDelegate
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">int</span><span style="color: #002200;">&#41;</span>numberOfItems;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span>UIImage<span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>coverImageForIndex<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">int</span><span style="color: #002200;">&#41;</span>index;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span>UIImage<span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>placeholderImage;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>coverHasChangedToIndex<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">int</span><span style="color: #002200;">&#41;</span>index;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>clickOnCoverWithIndex<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">int</span><span style="color: #002200;">&#41;</span>index;
<span style="color: #a61390;">@end</span>
&nbsp;
<span style="color: #a61390;">@interface</span> RBCoverFlowView <span style="color: #002200;">:</span> UIView <span style="color: #002200;">&#123;</span>
	CGPoint _startTouchPosition;
	<span style="color: #a61390;">float</span> _centerPosition;
	<span style="color: #a61390;">float</span> _sidePosition;
	CATransform3D _leftTransform;
	CATransform3D _rightTransform;
&nbsp;
	CALayer<span style="color: #002200;">*</span> _contentLayer;
&nbsp;
	<span style="color: #a61390;">id</span> _delegate;
	<span style="color: #a61390;">int</span> _itemCount;
&nbsp;
	CGImageRef _placeholderImageRef;
	UIImage<span style="color: #002200;">*</span> _placeholderImage;
	CGImageRef _maskedPlaceholderImageRef;
&nbsp;
	CGSize _imageSize;
	CGFloat _imageScaleX;
	CGFloat _imageScaleY;
&nbsp;
	<span style="color: #a61390;">float</span> _rowScaleFactor;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #11740a; font-style: italic;">// Start the cover flow</span>
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>start<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>delegate;
&nbsp;
<span style="color: #a61390;">@end</span></pre></div></div>

<p>Der Cover Flow besteht zunächst aus einem &#8220;Hauptview&#8221;, der von UIView abgeleitet ist und in dem die Covers dargestellt werden. Dieser ist zudem für das Verarbeiten der Touches und der gesamten Animation zuständig.</p>
<p>Die Covers selber bestehen aus einzelnen Layern, bzw. genau drei Layern pro Cover. Der Layer mit dem Bild selber, ein Layer, der die Spiegelung (Reflection) darstellt und ein Rahmen-Layer, der das ganze Cover umschliesst.</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span>CALayer <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>addCoverLayer<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">int</span><span style="color: #002200;">&#41;</span>index <span style="color: #002200;">&#123;</span>
	CALayer<span style="color: #002200;">*</span> containerLayer <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CALayer layer<span style="color: #002200;">&#93;</span>;
	containerLayer.name <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSString</span> stringWithFormat<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;%d&quot;</span>, index<span style="color: #002200;">&#93;</span>;
	containerLayer.frame <span style="color: #002200;">=</span> CGRectMake<span style="color: #002200;">&#40;</span>0.0f, 0.0f, <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>self maxAvalibleImageWidth<span style="color: #002200;">&#93;</span> floatValue<span style="color: #002200;">&#93;</span>, 1.5f <span style="color: #002200;">*</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>self maxAvalibleImageHeight<span style="color: #002200;">&#93;</span> floatValue<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span>;
	containerLayer.contentsGravity <span style="color: #002200;">=</span> kCAGravityResize;
&nbsp;
	CALayer<span style="color: #002200;">*</span> holderLayer <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CALayer layer<span style="color: #002200;">&#93;</span>;
	holderLayer.name <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSString</span> stringWithFormat<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Holder_%d&quot;</span>, index<span style="color: #002200;">&#93;</span>;
	holderLayer.frame <span style="color: #002200;">=</span> CGRectMake<span style="color: #002200;">&#40;</span>0.0f, 0.0f, <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>self maxAvalibleImageWidth<span style="color: #002200;">&#93;</span> floatValue<span style="color: #002200;">&#93;</span>, <span style="color: #2400d9;">1.5</span> <span style="color: #002200;">*</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>self maxAvalibleImageHeight<span style="color: #002200;">&#93;</span> floatValue<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span>;
	holderLayer.contentsGravity <span style="color: #002200;">=</span> kCAGravityResize;
&nbsp;
	CALayer<span style="color: #002200;">*</span> imageLayer <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CALayer layer<span style="color: #002200;">&#93;</span>;
	imageLayer.name <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSString</span> stringWithFormat<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Image_%d&quot;</span>, index<span style="color: #002200;">&#93;</span>;
	imageLayer.contents <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>_placeholderImageRef;
	imageLayer.frame <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>self imageRect<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>CGImageRef<span style="color: #002200;">&#41;</span>imageLayer.contents<span style="color: #002200;">&#93;</span>;
	imageLayer.contentsGravity <span style="color: #002200;">=</span> kCAGravityResizeAspectFill;
	imageLayer.masksToBounds <span style="color: #002200;">=</span> <span style="color: #a61390;">YES</span>;
&nbsp;
	CALayer<span style="color: #002200;">*</span> reflectionLayer <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CALayer layer<span style="color: #002200;">&#93;</span>;
	reflectionLayer.name <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSString</span> stringWithFormat<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Reflection_%d&quot;</span>, index<span style="color: #002200;">&#93;</span>;
	reflectionLayer.contents <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>_maskedPlaceholderImageRef;
	CGRect frame <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>self imageRect<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>CGImageRef<span style="color: #002200;">&#41;</span>reflectionLayer.contents<span style="color: #002200;">&#93;</span>;
	frame.origin.y <span style="color: #002200;">=</span> imageLayer.frame.size.height <span style="color: #002200;">+</span> <span style="color: #2400d9;">2</span>;
	reflectionLayer.frame <span style="color: #002200;">=</span> frame;
	reflectionLayer.contentsGravity <span style="color: #002200;">=</span> kCAGravityResize;
	reflectionLayer.masksToBounds <span style="color: #002200;">=</span> <span style="color: #a61390;">YES</span>;
	reflectionLayer.transform <span style="color: #002200;">=</span> CATransform3DMakeScale<span style="color: #002200;">&#40;</span>1.0f, <span style="color: #002200;">-</span>1.0f, 1.0f<span style="color: #002200;">&#41;</span>;
	reflectionLayer.opacity <span style="color: #002200;">=</span> <span style="color: #2400d9;">0.6</span>;
&nbsp;
	<span style="color: #002200;">&#91;</span>containerLayer setValue<span style="color: #002200;">:</span>imageLayer forKey<span style="color: #002200;">:</span>kImageLayerKey<span style="color: #002200;">&#93;</span>;
	<span style="color: #002200;">&#91;</span>containerLayer setValue<span style="color: #002200;">:</span>reflectionLayer forKey<span style="color: #002200;">:</span>kImageReflectionKey<span style="color: #002200;">&#93;</span>;
	<span style="color: #002200;">&#91;</span>containerLayer setValue<span style="color: #002200;">:</span>holderLayer forKey<span style="color: #002200;">:</span>kImageHolderKey<span style="color: #002200;">&#93;</span>;
&nbsp;
	<span style="color: #002200;">&#91;</span>holderLayer addSublayer<span style="color: #002200;">:</span>imageLayer<span style="color: #002200;">&#93;</span>;
	<span style="color: #002200;">&#91;</span>holderLayer addSublayer<span style="color: #002200;">:</span>reflectionLayer<span style="color: #002200;">&#93;</span>;
	<span style="color: #002200;">&#91;</span>containerLayer addSublayer<span style="color: #002200;">:</span>holderLayer<span style="color: #002200;">&#93;</span>;
&nbsp;
	<span style="color: #a61390;">return</span> containerLayer;
<span style="color: #002200;">&#125;</span></pre></div></div>

<p>Sind die Layer erstellt worden, müssen die einzelnen Cover nun entsprechend positioniert werden, damit das Aussehen dem Cover Flow der iPod App entspricht. Dies erreicht man, in dem man jeden einzelnen Layer im Raum positioniert, skaliert und leicht dreht.</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>layoutSublayer<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>CALayer<span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>rootLayer number<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">int</span><span style="color: #002200;">&#41;</span>number <span style="color: #002200;">&#123;</span>
	<span style="color: #a61390;">int</span> selectedIndex <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>_contentLayer valueForKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;selectedIndex&quot;</span><span style="color: #002200;">&#93;</span> integerValue<span style="color: #002200;">&#93;</span>;
&nbsp;
	rootLayer.frame <span style="color: #002200;">=</span> CGRectMake<span style="color: #002200;">&#40;</span>0.0f, 0.0f, <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>self maxAvalibleImageWidth<span style="color: #002200;">&#93;</span> floatValue<span style="color: #002200;">&#93;</span>, 1.5f <span style="color: #002200;">*</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>self maxAvalibleImageHeight<span style="color: #002200;">&#93;</span> floatValue<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span>;
&nbsp;
	CATransform3D layerTransform <span style="color: #002200;">=</span> CATransform3DIdentity;
	layerTransform.m34 <span style="color: #002200;">=</span> 1.0f <span style="color: #002200;">/</span> _sidePosition;
&nbsp;
	CALayer<span style="color: #002200;">*</span> holder <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>rootLayer valueForKey<span style="color: #002200;">:</span>kImageHolderKey<span style="color: #002200;">&#93;</span>;
&nbsp;
	CGPoint pos <span style="color: #002200;">=</span> CGPointMake<span style="color: #002200;">&#40;</span>CGRectGetMidX<span style="color: #002200;">&#40;</span>_contentLayer.bounds<span style="color: #002200;">&#41;</span>, CGRectGetMidY<span style="color: #002200;">&#40;</span>_contentLayer.bounds<span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>;
&nbsp;
	<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>number <span style="color: #002200;">&amp;</span>lt; selectedIndex<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
		pos.x <span style="color: #002200;">-=</span> <span style="color: #002200;">&#40;</span>selectedIndex <span style="color: #002200;">-</span> number<span style="color: #002200;">&#41;</span> <span style="color: #002200;">*</span> _imageSize.width <span style="color: #002200;">*</span> _rowScaleFactor;
		holder.transform <span style="color: #002200;">=</span> _leftTransform;
		holder.zPosition <span style="color: #002200;">=</span> _sidePosition;
		rootLayer.zPosition <span style="color: #002200;">=</span> _sidePosition <span style="color: #002200;">-</span> 0.1f <span style="color: #002200;">*</span> <span style="color: #002200;">&#40;</span>selectedIndex <span style="color: #002200;">-</span> number<span style="color: #002200;">&#41;</span>;
		rootLayer.sublayerTransform <span style="color: #002200;">=</span> layerTransform;
	<span style="color: #002200;">&#125;</span> <span style="color: #a61390;">else</span> <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>number <span style="color: #002200;">&amp;</span>gt; selectedIndex<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
		pos.x <span style="color: #002200;">+=</span> <span style="color: #002200;">&#40;</span>number <span style="color: #002200;">-</span> selectedIndex<span style="color: #002200;">&#41;</span> <span style="color: #002200;">*</span> _imageSize.width <span style="color: #002200;">*</span> _rowScaleFactor;
		holder.transform <span style="color: #002200;">=</span> _rightTransform;
		holder.zPosition <span style="color: #002200;">=</span> _sidePosition;
		rootLayer.zPosition <span style="color: #002200;">=</span> _sidePosition <span style="color: #002200;">-</span> 0.1f <span style="color: #002200;">*</span> <span style="color: #002200;">&#40;</span>number <span style="color: #002200;">-</span> selectedIndex<span style="color: #002200;">&#41;</span>;
		rootLayer.sublayerTransform <span style="color: #002200;">=</span> layerTransform;
	<span style="color: #002200;">&#125;</span> <span style="color: #a61390;">else</span> <span style="color: #002200;">&#123;</span>
		pos.x <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span>self.bounds.size.width <span style="color: #002200;">-</span> _imageSize.width<span style="color: #002200;">&#41;</span> <span style="color: #002200;">/</span> <span style="color: #2400d9;">2</span>;
		holder.transform <span style="color: #002200;">=</span> CATransform3DIdentity;
		holder.zPosition <span style="color: #002200;">=</span> _centerPosition;
		holder.filters <span style="color: #002200;">=</span> <span style="color: #a61390;">nil</span>;
		rootLayer.zPosition <span style="color: #002200;">=</span> _centerPosition;
		rootLayer.sublayerTransform <span style="color: #002200;">=</span> CATransform3DIdentity;
	<span style="color: #002200;">&#125;</span>
&nbsp;
	rootLayer.position <span style="color: #002200;">=</span> pos;
<span style="color: #002200;">&#125;</span></pre></div></div>

<p>Die Animation ist in dem Code (Hier sind die kompletten <a href="http://www.rogerboesch.com/tutorials/CoverFlowSource.zip">Source Files</a>) bewusst recht einfach gehalten und unterstützt keine sogenannten Heartbeat Animationen, d.h. Animationen die sogleich beginnen, wenn der Finger aufgesetzt wird. Im käuflichen und auf OpenGL basierten Code ist dies selbstverständlich vorhanden und insbesondere das Laden und Aufbauen der Bilder noch stark optimiert. Aber als Einstieg und fürs bessere Verständnis hoffe ich, dass der Beispielcode hier erstmal weiterhilft. Wer mehr darüber erfahren möchte, dem möchte ich nochmals unsere <a href="http://www.rogerboesch.com/workshops/">Workshops</a> ans Herz legen, in denen wir wesentlich tiefer in die Materie einsteigen können. Also, viel Spass beim Cover Flow programmieren und nicht vergessen, auf mein Blog zu verlinken wenn Ihr die erste iApp mit Cover Flow in den Appstore bringt <img src='http://www.rogerboesch.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><em>Anfangs basierte dieser Code übrigens auf einem Beispiel Code von <a title='Original Link: http://bill.dudney.net/roller/objc/'  href="http://www.rogerboesch.com/?1haoVHxh">Bill Dudney</a>, der damals einen Artikel über die Programmierung eines Cover Flow unter MacOSX veröffenlichte und auch ein sehr gut geschriebenes Buch zum Thema Core Animation geschrieben hat.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.rogerboesch.com/2009/08/16/coverflow-programmieren/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
