Merge lp:~rockstar/ubuntuone-ios-music/polish-list-views into lp:ubuntuone-ios-music

Proposed by Paul Hummer
Status: Merged
Approved by: Paul Hummer
Approved revision: 292
Merged at revision: 249
Proposed branch: lp:~rockstar/ubuntuone-ios-music/polish-list-views
Merge into: lp:ubuntuone-ios-music
Prerequisite: lp:~rockstar/ubuntuone-ios-music/replace-streaming-player
Diff against target: 1800 lines (+1037/-204)
27 files modified
Dependencies/SSPullToRefresh/LICENSE (+20/-0)
Dependencies/SSPullToRefresh/SSPullToRefresh.h (+16/-0)
Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.h (+17/-0)
Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.m (+86/-0)
Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.h (+16/-0)
Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.m (+80/-0)
Dependencies/SSPullToRefresh/SSPullToRefreshView.h (+205/-0)
Dependencies/SSPullToRefresh/SSPullToRefreshView.m (+349/-0)
Music/Models/Album.h (+2/-0)
Music/Models/Album.m (+5/-0)
Music/Storyboard_iPhone.storyboard (+78/-160)
Music/Utilities/UOPlayer.h (+2/-0)
Music/Utilities/UOPlayer.m (+1/-1)
Music/View Controllers/AlbumViewController.h (+1/-0)
Music/View Controllers/AlbumViewController.m (+12/-2)
Music/View Controllers/AlbumsViewController.m (+5/-0)
Music/View Controllers/ArtistViewController.m (+11/-5)
Music/View Controllers/ArtistsViewController.m (+4/-0)
Music/View Controllers/PlayerViewController.m (+2/-2)
Music/View Controllers/PlaylistsViewController.m (+4/-0)
Music/View Controllers/SongsViewController.m (+10/-6)
Music/View Controllers/UOIndexedViewController.h (+1/-0)
Music/View Controllers/UOIndexedViewController.m (+23/-27)
Music/Views/Table Cells/ArtistCell.m (+2/-0)
Music/Views/Table Cells/SongListCell.h (+15/-0)
Music/Views/Table Cells/SongListCell.m (+34/-0)
U1Music.xcodeproj/project.pbxproj (+36/-1)
To merge this branch: bzr merge lp:~rockstar/ubuntuone-ios-music/polish-list-views
Reviewer Review Type Date Requested Status
Mike McCracken (community) Approve
Review via email: mp+147028@code.launchpad.net

Commit message

Polish the list views

Description of the change

I just did a bunch of tweaks that make things more presentable on the list views and such. I changed the font to be more in line with what design had envisioned in the original settings view (and the new settings view followed suit), and made things a bit more pixel lined up (using Xcode's guides).

I also fixed a bug where you'd navigate from an artist, to a various artist album and the album listing shows all the songs, rather than filtered down to only the artist's songs on that album.

To post a comment you must log in.
Revision history for this message
Mike McCracken (mikemc) wrote :

Separate changes I see in here that could have been multiple branches:
- adding SSPullToRefresh
- songsForArtistID bug fix
- songlistcell art downloading

1. The license for sspulltorefresh probably needs to be included:
https://github.com/soffes/sspulltorefresh/blob/master/LICENSE

1a. Do we need the GPL on each of the source files? That's what we do
in the python apps...

2. Do we localize this app? The hard-coded english pluralization made
me wonder.

3. In SongListCell.m, should the downloader completionBlock also set
song.art? Otherwise it looks like we'll be downloading it every time
it shows up...

review: Needs Information
288. By Paul Hummer

Merge from previous pipe

289. By Paul Hummer

Merged replace-streaming-player into polish-list-views.

290. By Paul Hummer

Merged replace-streaming-player into polish-list-views.

291. By Paul Hummer

Reset the detailTextLabel

292. By Paul Hummer

Add SSPullToRefresh LICENSE

Revision history for this message
Paul Hummer (rockstar) wrote :

With small changes, I don't see any problem in putting those changes in the same branch.

(1) Added LICENSE
(1a) I'll add the GPL headers to files prior to release. I'm trying to keep the diff size down, especially with stuff that is not needed in code review.
(2) We aren't currently focused on localization in any of the Ubuntu One properties. It might be nice in the future, but not now.
(3) art is actually a method that checks the filesystem for the file itself, so it won't get downloaded again. There is a chance that it'll get downloaded and re-written, but that's super unlikely at this point, and there are bigger problems with the album art (like storing multiple copies of the default album art...) At some point, I'll make the downloader smarter, and not queue a download if it already is queued.

Revision history for this message
Mike McCracken (mikemc) wrote :

I won't fight you on branch size - I'll just explain my bias toward branches as small as possible.
On the desktop projects, we've tried pretty hard to make separate branches out of separate changes, and I've noticed that they're easier to review, issues with one change don't block landing other changes, and when doing bzr-blame log searching while fixing bugs, big multi-feature branches make tracing and understanding historical changes harder.

We also had a size guideline at one point: we decided to try to keep branches ~500 lines or less, even to the point of committing half of a feature in one branch and the other half in another. I thought that extreme was overkill (obviously e.g. adding a framework with headers is going to be big), and the specific number that makes sense will be different for different languages, but 500 seemed like a good rule of thumb.

Thanks for taking the time to write a detailed reply, too. I appreciate it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'Dependencies/SSPullToRefresh'
2=== added file 'Dependencies/SSPullToRefresh/LICENSE'
3--- Dependencies/SSPullToRefresh/LICENSE 1970-01-01 00:00:00 +0000
4+++ Dependencies/SSPullToRefresh/LICENSE 2013-02-11 04:30:25 +0000
5@@ -0,0 +1,20 @@
6+Copyright (c) 2012 Sam Soffes
7+
8+Permission is hereby granted, free of charge, to any person obtaining
9+a copy of this software and associated documentation files (the
10+"Software"), to deal in the Software without restriction, including
11+without limitation the rights to use, copy, modify, merge, publish,
12+distribute, sublicense, and/or sell copies of the Software, and to
13+permit persons to whom the Software is furnished to do so, subject to
14+the following conditions:
15+
16+The above copyright notice and this permission notice shall be
17+included in all copies or substantial portions of the Software.
18+
19+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
27=== added file 'Dependencies/SSPullToRefresh/SSPullToRefresh.h'
28--- Dependencies/SSPullToRefresh/SSPullToRefresh.h 1970-01-01 00:00:00 +0000
29+++ Dependencies/SSPullToRefresh/SSPullToRefresh.h 2013-02-11 04:30:25 +0000
30@@ -0,0 +1,16 @@
31+//
32+// SSPullToRefresh.h
33+// SSPullToRefresh
34+//
35+// Created by Sam Soffes on 4/9/12.
36+// Copyright (c) 2012 Sam Soffes. All rights reserved.
37+//
38+
39+// Main pull to refresh view. This class contains all of the pulling logic.
40+#import "SSPullToRefreshView.h"
41+
42+// Default content view. Similar to Facebook.
43+#import "SSPullToRefreshDefaultContentView.h"
44+
45+// Simple content view. Similar to Path.
46+#import "SSPullToRefreshSimpleContentView.h"
47
48=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.h'
49--- Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.h 1970-01-01 00:00:00 +0000
50+++ Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.h 2013-02-11 04:30:25 +0000
51@@ -0,0 +1,17 @@
52+//
53+// SSPullToRefreshDefaultContentView
54+// SSPullToRefresh
55+//
56+// Created by Sam Soffes on 4/9/12.
57+// Copyright (c) 2012 Sam Soffes. All rights reserved.
58+//
59+
60+#import "SSPullToRefreshView.h"
61+
62+@interface SSPullToRefreshDefaultContentView : UIView <SSPullToRefreshContentView>
63+
64+@property (nonatomic, strong, readonly) UILabel *statusLabel;
65+@property (nonatomic, strong, readonly) UILabel *lastUpdatedAtLabel;
66+@property (nonatomic, strong, readonly) UIActivityIndicatorView *activityIndicatorView;
67+
68+@end
69
70=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.m'
71--- Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.m 1970-01-01 00:00:00 +0000
72+++ Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.m 2013-02-11 04:30:25 +0000
73@@ -0,0 +1,86 @@
74+//
75+// SSPullToRefreshDefaultContentView
76+// SSPullToRefresh
77+//
78+// Created by Sam Soffes on 4/9/12.
79+// Copyright (c) 2012 Sam Soffes. All rights reserved.
80+//
81+
82+#import "SSPullToRefreshDefaultContentView.h"
83+
84+@implementation SSPullToRefreshDefaultContentView
85+
86+@synthesize statusLabel = _statusLabel;
87+@synthesize lastUpdatedAtLabel = _lastUpdatedAtLabel;
88+@synthesize activityIndicatorView = _activityIndicatorView;
89+
90+#pragma mark - UIView
91+
92+- (id)initWithFrame:(CGRect)frame {
93+ if ((self = [super initWithFrame:frame])) {
94+ CGFloat width = self.bounds.size.width;
95+
96+ _statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 14.0f, width, 20.0f)];
97+ _statusLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
98+ _statusLabel.font = [UIFont boldSystemFontOfSize:14.0f];
99+ _statusLabel.textColor = [UIColor blackColor];
100+ _statusLabel.backgroundColor = [UIColor clearColor];
101+ _statusLabel.textAlignment = UITextAlignmentCenter;
102+ [self addSubview:_statusLabel];
103+
104+ _lastUpdatedAtLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 34.0f, width, 20.0f)];
105+ _lastUpdatedAtLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
106+ _lastUpdatedAtLabel.font = [UIFont systemFontOfSize:12.0f];
107+ _lastUpdatedAtLabel.textColor = [UIColor lightGrayColor];
108+ _lastUpdatedAtLabel.backgroundColor = [UIColor clearColor];
109+ _lastUpdatedAtLabel.textAlignment = UITextAlignmentCenter;
110+ [self addSubview:_lastUpdatedAtLabel];
111+
112+ _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
113+ _activityIndicatorView.frame = CGRectMake(30.0f, 25.0f, 20.0f, 20.0f);
114+ [self addSubview:_activityIndicatorView];
115+ }
116+ return self;
117+}
118+
119+
120+#pragma mark - SSPullToRefreshContentView
121+
122+- (void)setState:(SSPullToRefreshViewState)state withPullToRefreshView:(SSPullToRefreshView *)view {
123+ switch (state) {
124+ case SSPullToRefreshViewStateReady: {
125+ _statusLabel.text = @"Release to refresh...";
126+ [_activityIndicatorView stopAnimating];
127+ break;
128+ }
129+
130+ case SSPullToRefreshViewStateNormal: {
131+ _statusLabel.text = @"Pull down to refresh...";
132+ [_activityIndicatorView stopAnimating];
133+ break;
134+ }
135+
136+ case SSPullToRefreshViewStateLoading:
137+ case SSPullToRefreshViewStateClosing: {
138+ _statusLabel.text = @"Loading...";
139+ [_activityIndicatorView startAnimating];
140+ break;
141+ }
142+ }
143+}
144+
145+
146+- (void)setLastUpdatedAt:(NSDate *)date withPullToRefreshView:(SSPullToRefreshView *)view {
147+ static NSDateFormatter *dateFormatter = nil;
148+ static dispatch_once_t onceToken;
149+ dispatch_once(&onceToken, ^{
150+ dateFormatter = [[NSDateFormatter alloc] init];
151+ dateFormatter.formatterBehavior = NSDateFormatterBehavior10_4;
152+ dateFormatter.dateStyle = NSDateFormatterLongStyle;
153+ dateFormatter.timeStyle = NSDateFormatterShortStyle;
154+ });
155+
156+ _lastUpdatedAtLabel.text = [NSString stringWithFormat:@"Last Updated: %@", [dateFormatter stringForObjectValue:date]];
157+}
158+
159+@end
160
161=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.h'
162--- Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.h 1970-01-01 00:00:00 +0000
163+++ Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.h 2013-02-11 04:30:25 +0000
164@@ -0,0 +1,16 @@
165+//
166+// SSPullToRefreshSimpleContentView.h
167+// SSPullToRefresh
168+//
169+// Created by Sam Soffes on 5/17/12.
170+// Copyright (c) 2012 Sam Soffes. All rights reserved.
171+//
172+
173+#import "SSPullToRefreshView.h"
174+
175+@interface SSPullToRefreshSimpleContentView : UIView <SSPullToRefreshContentView>
176+
177+@property (nonatomic, strong, readonly) UILabel *statusLabel;
178+@property (nonatomic, strong, readonly) UIActivityIndicatorView *activityIndicatorView;
179+
180+@end
181
182=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.m'
183--- Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.m 1970-01-01 00:00:00 +0000
184+++ Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.m 2013-02-11 04:30:25 +0000
185@@ -0,0 +1,80 @@
186+//
187+// SSPullToRefreshSimpleContentView.m
188+// SSPullToRefresh
189+//
190+// Created by Sam Soffes on 5/17/12.
191+// Copyright (c) 2012 Sam Soffes. All rights reserved.
192+//
193+
194+#import "SSPullToRefreshSimpleContentView.h"
195+
196+@implementation SSPullToRefreshSimpleContentView
197+
198+@synthesize statusLabel = _statusLabel;
199+@synthesize activityIndicatorView = _activityIndicatorView;
200+
201+
202+#pragma mark - UIView
203+
204+- (id)initWithFrame:(CGRect)frame {
205+ if ((self = [super initWithFrame:frame])) {
206+ CGFloat width = self.bounds.size.width;
207+
208+ _statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 14.0f, width, 20.0f)];
209+ _statusLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
210+ _statusLabel.font = [UIFont boldSystemFontOfSize:14.0f];
211+ _statusLabel.textColor = [UIColor blackColor];
212+ _statusLabel.backgroundColor = [UIColor clearColor];
213+ _statusLabel.textAlignment = UITextAlignmentCenter;
214+ [self addSubview:_statusLabel];
215+
216+ _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
217+ _activityIndicatorView.frame = CGRectMake(30.0f, 25.0f, 20.0f, 20.0f);
218+ [self addSubview:_activityIndicatorView];
219+ }
220+ return self;
221+}
222+
223+
224+- (void)layoutSubviews {
225+ CGSize size = self.bounds.size;
226+ self.statusLabel.frame = CGRectMake(20.0f, roundf((size.height - 30.0f) / 2.0f), size.width - 40.0f, 30.0f);
227+ self.activityIndicatorView.frame = CGRectMake(roundf((size.width - 20.0f) / 2.0f), roundf((size.height - 20.0f) / 2.0f), 20.0f, 20.0f);
228+}
229+
230+
231+#pragma mark - SSPullToRefreshContentView
232+
233+- (void)setState:(SSPullToRefreshViewState)state withPullToRefreshView:(SSPullToRefreshView *)view {
234+ switch (state) {
235+ case SSPullToRefreshViewStateReady: {
236+ self.statusLabel.text = @"Release to refresh";
237+ [self.activityIndicatorView startAnimating];
238+ self.activityIndicatorView.alpha = 0.0f;
239+ break;
240+ }
241+
242+ case SSPullToRefreshViewStateNormal: {
243+ self.statusLabel.text = @"Pull down to refresh";
244+ self.statusLabel.alpha = 1.0f;
245+ [self.activityIndicatorView stopAnimating];
246+ self.activityIndicatorView.alpha = 0.0f;
247+ break;
248+ }
249+
250+ case SSPullToRefreshViewStateLoading: {
251+ self.statusLabel.alpha = 0.0f;
252+ [self.activityIndicatorView startAnimating];
253+ self.activityIndicatorView.alpha = 1.0f;
254+ break;
255+ }
256+
257+ case SSPullToRefreshViewStateClosing: {
258+ self.statusLabel.text = nil;
259+ self.activityIndicatorView.alpha = 0.0f;
260+ break;
261+ }
262+ }
263+}
264+
265+@end
266
267=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshView.h'
268--- Dependencies/SSPullToRefresh/SSPullToRefreshView.h 1970-01-01 00:00:00 +0000
269+++ Dependencies/SSPullToRefresh/SSPullToRefreshView.h 2013-02-11 04:30:25 +0000
270@@ -0,0 +1,205 @@
271+//
272+// SSPullToRefreshView.h
273+// SSPullToRefresh
274+//
275+// Created by Sam Soffes on 4/9/12.
276+// Copyright (c) 2012 Sam Soffes. All rights reserved.
277+//
278+
279+//
280+// Example usage:
281+//
282+// - (void)viewDidLoad {
283+// [super viewDidLoad];
284+// self.pullToRefreshView = [[SSPullToRefreshView alloc] initWithScrollView:self.tableView delegate:self];
285+// }
286+//
287+// - (void)viewDidUnload {
288+// [super viewDidUnload];
289+// self.pullToRefreshView = nil;
290+// }
291+//
292+// - (void)refresh {
293+// [self.pullToRefreshView startLoading];
294+// // Load data...
295+// [self.pullToRefreshView finishLoading];
296+// }
297+//
298+// - (void)pullToRefreshViewDidStartLoading:(SSPullToRefreshView *)view {
299+// [self refresh];
300+// }
301+//
302+
303+typedef enum {
304+ /// Most will say "Pull to refresh" in this state
305+ SSPullToRefreshViewStateNormal,
306+
307+ /// Most will say "Release to refresh" in this state
308+ SSPullToRefreshViewStateReady,
309+
310+ /// The view is loading
311+ SSPullToRefreshViewStateLoading,
312+
313+ /// The view has finished loading and is animating
314+ SSPullToRefreshViewStateClosing
315+} SSPullToRefreshViewState;
316+
317+@protocol SSPullToRefreshViewDelegate;
318+@protocol SSPullToRefreshContentView;
319+
320+@interface SSPullToRefreshView : UIView
321+
322+/**
323+ The content view displayed when the `scrollView` is pulled down. By default this is an instance of `SSPullToRefreshDefaultContentView`.
324+
325+ @see SSPullToRefreshContentView
326+ */
327+@property (nonatomic, strong) UIView<SSPullToRefreshContentView> *contentView;
328+
329+/**
330+ If you need to update the scroll view's content inset while it contains a pull to refresh view, you should set the
331+ `defaultContentInset` on the pull to refresh view and it will forward it to the scroll view taking into account the
332+ pull to refresh view's position.
333+ */
334+@property (nonatomic, assign) UIEdgeInsets defaultContentInset;
335+
336+/**
337+ The height of the fully expanded content view. The default is `70.0`.
338+
339+ The `contentView`'s `sizeThatFits:` will be respected when displayed but does not effect the expanded height. You can use this
340+ to draw outside of the expanded area. If you don't implement `sizeThatFits:` it will automatically display at the default size.
341+
342+ @see expanded
343+ */
344+@property (nonatomic, assign) CGFloat expandedHeight;
345+
346+/**
347+ A boolean indicating if the pull to refresh view is expanded.
348+
349+ @see expandedHeight
350+ @see startLoadingAndExpand:
351+ */
352+@property (nonatomic, assign, readonly, getter = isExpanded) BOOL expanded;
353+
354+/**
355+ The scroll view containing the pull to refresh view. This is automatically set with `initWithScrollView:delegate:`.
356+
357+ @see initWithScrollView:delegate:
358+ */
359+@property (nonatomic, assign, readonly) UIScrollView *scrollView;
360+
361+/**
362+ The delegate is sent messages when the pull to refresh view starts loading. This is automatically set with `initWithScrollView:delegate:`.
363+
364+ @see initWithScrollView:delegate:
365+ @see SSPullToRefreshViewDelegate
366+ */
367+@property (nonatomic, weak) id<SSPullToRefreshViewDelegate> delegate;
368+
369+/**
370+ The state of the pull to refresh view.
371+
372+ @see startLoading
373+ @see startLoadingAndExpand:
374+ @see finishLoading
375+ @see SSPullToRefreshViewState
376+ */
377+@property (nonatomic, assign, readonly) SSPullToRefreshViewState state;
378+
379+/**
380+ All you need to do to add this view to your scroll view is call this method (passing in the scroll view). That's it.
381+ You don't have to add it as subview or anything else. The rest is magic.
382+
383+ You should only initalize with this method and never move it to another scroll view during its lifetime.
384+ */
385+- (id)initWithScrollView:(UIScrollView *)scrollView delegate:(id<SSPullToRefreshViewDelegate>)delegate;
386+
387+/**
388+ Call this method when you start loading. If you trigger loading another way besides pulling to refresh, call this
389+ method so the pull to refresh view will be in sync with the loading status. By default, it will not expand the view
390+ so it loads quietly out of view.
391+ */
392+- (void)startLoading;
393+
394+/**
395+ Call this method when you start loading. If you trigger loading another way besides pulling to refresh, call this
396+ method so the pull to refresh view will be in sync with the loading status. You may pass YES for shouldExpand to
397+ animate down the pull to refresh view to show that it's loading.
398+ */
399+- (void)startLoadingAndExpand:(BOOL)shouldExpand;
400+/**
401+ Call this method if you wish to control animating the expansion.
402+ */
403+- (void)startLoadingAndExpand:(BOOL)shouldExpand animated:(BOOL)animated;
404+
405+/**
406+ Call this when you finish loading.
407+ */
408+- (void)finishLoading;
409+
410+/**
411+ Manually update the last updated at time. This will automatically get called when the pull to refresh view finishes laoding.
412+ */
413+- (void)refreshLastUpdatedAt;
414+
415+@end
416+
417+
418+@protocol SSPullToRefreshViewDelegate <NSObject>
419+
420+@optional
421+
422+/**
423+ Return `NO` if the pull to refresh view should no start loading.
424+ */
425+- (BOOL)pullToRefreshViewShouldStartLoading:(SSPullToRefreshView *)view;
426+
427+/**
428+ The pull to refresh view started loading. You should kick off whatever you need to load when this is called.
429+ */
430+- (void)pullToRefreshViewDidStartLoading:(SSPullToRefreshView *)view;
431+
432+/**
433+ The pull to refresh view finished loading. This will get called when it receives `finishLoading`.
434+ */
435+- (void)pullToRefreshViewDidFinishLoading:(SSPullToRefreshView *)view;
436+
437+/**
438+ The date when data was last updated. This will get called when it finishes loading or if it receives `refreshLastUpdatedAt`.
439+ Some content views may display this date.
440+ */
441+- (NSDate *)pullToRefreshViewLastUpdatedAt:(SSPullToRefreshView *)view;
442+
443+/**
444+ The pull to refresh view updated its scroll view's content inset
445+ */
446+- (void)pullToRefreshView:(SSPullToRefreshView *)view didUpdateContentInset:(UIEdgeInsets)contentInset;
447+
448+@end
449+
450+
451+@protocol SSPullToRefreshContentView <NSObject>
452+
453+@required
454+
455+/**
456+ The pull to refresh view's state has changed. The content view must update itself. All content view's must implement
457+ this method.
458+ */
459+- (void)setState:(SSPullToRefreshViewState)state withPullToRefreshView:(SSPullToRefreshView *)view;
460+
461+@optional
462+
463+/**
464+ The pull to refresh view will set send values from `0.0` to `1.0` as the user pulls down. `1.0` means it is fully expanded and
465+ will change to the `SSPullToRefreshViewStateReady` state. You can use this value to draw the progress of the pull
466+ (i.e. Tweetbot style).
467+ */
468+- (void)setPullProgress:(CGFloat)pullProgress;
469+
470+/**
471+ The pull to refresh view updated its last updated date.
472+ */
473+- (void)setLastUpdatedAt:(NSDate *)date withPullToRefreshView:(SSPullToRefreshView *)view;
474+
475+@end
476
477=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshView.m'
478--- Dependencies/SSPullToRefresh/SSPullToRefreshView.m 1970-01-01 00:00:00 +0000
479+++ Dependencies/SSPullToRefresh/SSPullToRefreshView.m 2013-02-11 04:30:25 +0000
480@@ -0,0 +1,349 @@
481+//
482+// SSPullToRefreshView.m
483+// SSPullToRefresh
484+//
485+// Created by Sam Soffes on 4/9/12.
486+// Copyright (c) 2012 Sam Soffes. All rights reserved.
487+//
488+
489+#import "SSPullToRefreshView.h"
490+#import "SSPullToRefreshDefaultContentView.h"
491+
492+@interface SSPullToRefreshView ()
493+@property (nonatomic, assign, readwrite) SSPullToRefreshViewState state;
494+@property (nonatomic, assign, readwrite) UIScrollView *scrollView;
495+@property (nonatomic, assign, readwrite, getter = isExpanded) BOOL expanded;
496+- (void)_setContentInsetTop:(CGFloat)topInset;
497+- (void)_setState:(SSPullToRefreshViewState)state animated:(BOOL)animated expanded:(BOOL)expanded completion:(void (^)(void))completion;
498+- (void)_setPullProgress:(CGFloat)pullProgress;
499+@end
500+
501+@implementation SSPullToRefreshView {
502+ dispatch_semaphore_t _animationSemaphore;
503+ CGFloat _topInset;
504+}
505+
506+@synthesize delegate = _delegate;
507+@synthesize scrollView = _scrollView;
508+@synthesize expandedHeight = _expandedHeight;
509+@synthesize contentView = _contentView;
510+@synthesize state = _state;
511+@synthesize expanded = _expanded;
512+@synthesize defaultContentInset = _defaultContentInset;
513+
514+
515+#pragma mark - Accessors
516+
517+- (void)setState:(SSPullToRefreshViewState)state {
518+ BOOL wasLoading = _state == SSPullToRefreshViewStateLoading;
519+ _state = state;
520+
521+ // Forward to content view
522+ [self.contentView setState:_state withPullToRefreshView:self];
523+
524+ // Update delegate
525+ if (wasLoading && _state != SSPullToRefreshViewStateLoading) {
526+ if ([_delegate respondsToSelector:@selector(pullToRefreshViewDidFinishLoading:)]) {
527+ [_delegate pullToRefreshViewDidFinishLoading:self];
528+ }
529+ } else if (!wasLoading && _state == SSPullToRefreshViewStateLoading) {
530+ [self _setPullProgress:1.0f];
531+ if ([_delegate respondsToSelector:@selector(pullToRefreshViewDidStartLoading:)]) {
532+ [_delegate pullToRefreshViewDidStartLoading:self];
533+ }
534+ }
535+}
536+
537+
538+- (void)setExpanded:(BOOL)expanded {
539+ _expanded = expanded;
540+ [self _setContentInsetTop:expanded ? self.expandedHeight : 0.0f];
541+}
542+
543+
544+- (void)setScrollView:(UIScrollView *)scrollView {
545+ void *context = (__bridge void *)self;
546+ if ([_scrollView respondsToSelector:@selector(removeObserver:forKeyPath:context:)]) {
547+ [_scrollView removeObserver:self forKeyPath:@"contentOffset" context:context];
548+ } else if (_scrollView) {
549+ [_scrollView removeObserver:self forKeyPath:@"contentOffset"];
550+ }
551+
552+ _scrollView = scrollView;
553+ _defaultContentInset = _scrollView.contentInset;
554+ [_scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:context];
555+}
556+
557+
558+- (UIView<SSPullToRefreshContentView> *)contentView {
559+ // Use the simple content view as the default
560+ if (!_contentView) {
561+ self.contentView = [[SSPullToRefreshDefaultContentView alloc] initWithFrame:CGRectZero];
562+ }
563+ return _contentView;
564+}
565+
566+
567+- (void)setContentView:(UIView<SSPullToRefreshContentView> *)contentView {
568+ [_contentView removeFromSuperview];
569+ _contentView = contentView;
570+
571+ _contentView.autoresizingMask = UIViewAutoresizingNone;
572+ [_contentView setState:_state withPullToRefreshView:self];
573+ [self refreshLastUpdatedAt];
574+ [self addSubview:_contentView];
575+}
576+
577+
578+- (void)setDefaultContentInset:(UIEdgeInsets)defaultContentInset {
579+ _defaultContentInset = defaultContentInset;
580+ [self _setContentInsetTop:_topInset];
581+}
582+
583+
584+#pragma mark - NSObject
585+
586+- (void)dealloc {
587+ self.scrollView = nil;
588+ self.delegate = nil;
589+#if !OS_OBJECT_USE_OBJC
590+ dispatch_release(_animationSemaphore);
591+#endif
592+}
593+
594+
595+#pragma mark - UIView
596+
597+- (void)removeFromSuperview {
598+ self.scrollView = nil;
599+ [super removeFromSuperview];
600+}
601+
602+
603+- (void)layoutSubviews {
604+ CGSize size = self.bounds.size;
605+ CGSize contentSize = [self.contentView sizeThatFits:size];
606+
607+ if (contentSize.width < size.width) {
608+ contentSize.width = size.width;
609+ }
610+
611+ if (contentSize.height < _expandedHeight) {
612+ contentSize.height = _expandedHeight;
613+ }
614+
615+ self.contentView.frame = CGRectMake(roundf((size.width - contentSize.width) / 2.0f), size.height - contentSize.height, contentSize.width, contentSize.height);
616+}
617+
618+
619+#pragma mark - Initializer
620+
621+- (id)initWithScrollView:(UIScrollView *)scrollView delegate:(id<SSPullToRefreshViewDelegate>)delegate {
622+ CGRect frame = CGRectMake(0.0f, 0.0f - scrollView.bounds.size.height, scrollView.bounds.size.width,
623+ scrollView.bounds.size.height);
624+ if ((self = [self initWithFrame:frame])) {
625+ self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
626+ self.scrollView = scrollView;
627+ self.delegate = delegate;
628+ self.state = SSPullToRefreshViewStateNormal;
629+ self.expandedHeight = 70.0f;
630+
631+ for (UIView *view in self.scrollView.subviews) {
632+ if ([view isKindOfClass:[SSPullToRefreshView class]]) {
633+ [[NSException exceptionWithName:@"SSPullToRefreshViewAlreadyAdded" reason:@"There is already a SSPullToRefreshView added to this scroll view. Unexpected things will happen. Don't do this." userInfo:nil] raise];
634+ }
635+ }
636+
637+ // Add to scroll view
638+ [self.scrollView addSubview:self];
639+
640+ // Semaphore is used to ensure only one animation plays at a time
641+ _animationSemaphore = dispatch_semaphore_create(0);
642+ dispatch_semaphore_signal(_animationSemaphore);
643+ }
644+ return self;
645+}
646+
647+
648+#pragma mark - Loading
649+
650+- (void)startLoading {
651+ [self startLoadingAndExpand:NO animated:NO];
652+}
653+
654+
655+- (void)startLoadingAndExpand:(BOOL)shouldExpand {
656+ [self startLoadingAndExpand:shouldExpand animated:YES];
657+}
658+
659+
660+- (void)startLoadingAndExpand:(BOOL)shouldExpand animated:(BOOL)animated {
661+ // If we're not loading, this method has no effect
662+ if (_state == SSPullToRefreshViewStateLoading) {
663+ return;
664+ }
665+
666+ // Animate back to the loading state
667+ [self _setState:SSPullToRefreshViewStateLoading animated:animated expanded:shouldExpand completion:nil];
668+}
669+
670+
671+- (void)finishLoading {
672+ // If we're not loading, this method has no effect
673+ if (_state != SSPullToRefreshViewStateLoading) {
674+ return;
675+ }
676+
677+ // Animate back to the normal state
678+ __weak SSPullToRefreshView *blockSelf = self;
679+ [self _setState:SSPullToRefreshViewStateClosing animated:YES expanded:NO completion:^{
680+ blockSelf.state = SSPullToRefreshViewStateNormal;
681+ }];
682+}
683+
684+
685+- (void)refreshLastUpdatedAt {
686+ NSDate *date = nil;
687+ if ([_delegate respondsToSelector:@selector(pullToRefreshViewLastUpdatedAt:)]) {
688+ date = [_delegate pullToRefreshViewLastUpdatedAt:self];
689+ } else {
690+ date = [NSDate date];
691+ }
692+
693+ // Forward to content view
694+ if ([self.contentView respondsToSelector:@selector(setLastUpdatedAt:withPullToRefreshView:)]) {
695+ [self.contentView setLastUpdatedAt:date withPullToRefreshView:self];
696+ }
697+}
698+
699+
700+#pragma mark - Private
701+
702+- (void)_setContentInsetTop:(CGFloat)topInset {
703+ _topInset = topInset;
704+
705+ // Default to the scroll view's initial content inset
706+ UIEdgeInsets inset = _defaultContentInset;
707+
708+ // Add the top inset
709+ inset.top += _topInset;
710+
711+ // Don't set it if that is already the current inset
712+ if (UIEdgeInsetsEqualToEdgeInsets(_scrollView.contentInset, inset)) {
713+ return;
714+ }
715+
716+ // Update the content inset
717+ _scrollView.contentInset = inset;
718+
719+ // If scrollView is on top, scroll again to the top (needed for scrollViews with content > scrollView).
720+ if (_scrollView.contentOffset.y == 0) {
721+ [_scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
722+ }
723+
724+ // Tell the delegate
725+ if ([self.delegate respondsToSelector:@selector(pullToRefreshView:didUpdateContentInset:)]) {
726+ [self.delegate pullToRefreshView:self didUpdateContentInset:_scrollView.contentInset];
727+ }
728+}
729+
730+
731+- (void)_setState:(SSPullToRefreshViewState)state animated:(BOOL)animated expanded:(BOOL)expanded completion:(void (^)(void))completion {
732+ if (!animated) {
733+ self.state = state;
734+ self.expanded = expanded;
735+
736+ if (completion) {
737+ completion();
738+ }
739+ return;
740+ }
741+
742+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
743+ dispatch_semaphore_wait(_animationSemaphore, DISPATCH_TIME_FOREVER);
744+ dispatch_async(dispatch_get_main_queue(), ^{
745+ [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
746+ self.state = state;
747+ self.expanded = expanded;
748+ } completion:^(BOOL finished) {
749+ dispatch_semaphore_signal(_animationSemaphore);
750+ if (completion) {
751+ completion();
752+ }
753+ }];
754+ });
755+ });
756+}
757+
758+
759+- (void)_setPullProgress:(CGFloat)pullProgress {
760+ if ([self.contentView respondsToSelector:@selector(setPullProgress:)]) {
761+ [self.contentView setPullProgress:pullProgress];
762+ }
763+}
764+
765+
766+#pragma mark - NSKeyValueObserving
767+
768+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
769+ // Call super if we didn't register for this notification
770+ if (context != (__bridge void *)self) {
771+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
772+ return;
773+ }
774+
775+ // We don't care about this notificaiton
776+ if (object != _scrollView || ![keyPath isEqualToString:@"contentOffset"]) {
777+ return;
778+ }
779+
780+ // Get the offset out of the change notification
781+ CGFloat y = [[change objectForKey:NSKeyValueChangeNewKey] CGPointValue].y + _defaultContentInset.top;
782+
783+ // Scroll view is dragging
784+ if (_scrollView.isDragging) {
785+ // Scroll view is ready
786+ if (_state == SSPullToRefreshViewStateReady) {
787+ // Dragged enough to refresh
788+ if (y > -_expandedHeight && y < 0.0f) {
789+ self.state = SSPullToRefreshViewStateNormal;
790+ }
791+ // Scroll view is normal
792+ } else if (_state == SSPullToRefreshViewStateNormal) {
793+ // Update the content view's pulling progressing
794+ [self _setPullProgress:-y / _expandedHeight];
795+
796+ // Dragged enough to be ready
797+ if (y < -_expandedHeight) {
798+ self.state = SSPullToRefreshViewStateReady;
799+ }
800+ // Scroll view is loading
801+ } else if (_state == SSPullToRefreshViewStateLoading) {
802+ [self _setContentInsetTop:_expandedHeight];
803+ }
804+ return;
805+ }
806+
807+ // If the scroll view isn't ready, we're not interested
808+ if (_state != SSPullToRefreshViewStateReady) {
809+ return;
810+ }
811+
812+ // We're ready, prepare to switch to loading. Be default, we should refresh.
813+ SSPullToRefreshViewState newState = SSPullToRefreshViewStateLoading;
814+
815+ // Ask the delegate if it's cool to start loading
816+ BOOL expand = YES;
817+ if ([_delegate respondsToSelector:@selector(pullToRefreshViewShouldStartLoading:)]) {
818+ if (![_delegate pullToRefreshViewShouldStartLoading:self]) {
819+ // Animate back to normal since the delegate said no
820+ newState = SSPullToRefreshViewStateNormal;
821+ expand = NO;
822+ }
823+ }
824+
825+ // Animate to the new state
826+ [self _setState:newState animated:YES expanded:expand completion:nil];
827+}
828+
829+@end
830
831=== modified file 'Music/Models/Album.h'
832--- Music/Models/Album.h 2013-01-24 19:08:51 +0000
833+++ Music/Models/Album.h 2013-02-11 04:30:25 +0000
834@@ -27,4 +27,6 @@
835
836 @property (nonatomic, retain) NSSet *songs;
837
838+- (NSSet *)songsForArtistId:(NSString *)artistId;
839+
840 @end
841
842=== modified file 'Music/Models/Album.m'
843--- Music/Models/Album.m 2013-01-24 19:08:51 +0000
844+++ Music/Models/Album.m 2013-02-11 04:30:25 +0000
845@@ -28,6 +28,11 @@
846
847 @dynamic songs;
848
849+- (NSSet *)songsForArtistId:(NSString *)_artistId {
850+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"artist.artistId == %@ OR albumArtist.artistId == %@", _artistId, _artistId];
851+ return [self.songs filteredSetUsingPredicate:predicate];
852+}
853+
854 #pragma mark - UOModel methods
855
856 + (RKEntityMapping *)objectMapping {
857
858=== modified file 'Music/Storyboard_iPhone.storyboard'
859--- Music/Storyboard_iPhone.storyboard 2013-02-11 04:30:25 +0000
860+++ Music/Storyboard_iPhone.storyboard 2013-02-11 04:30:25 +0000
861@@ -44,17 +44,17 @@
862 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
863 <autoresizingMask key="autoresizingMask"/>
864 <subviews>
865- <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Wy4-11-qhh">
866- <rect key="frame" x="53" y="2" width="38" height="22"/>
867+ <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Wy4-11-qhh">
868+ <rect key="frame" x="53" y="1" width="45" height="23"/>
869 <autoresizingMask key="autoresizingMask"/>
870- <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
871+ <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
872 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
873 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
874 </label>
875- <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="PUO-bG-eLt">
876- <rect key="frame" x="53" y="24" width="47" height="18"/>
877+ <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="2 albums" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="PUO-bG-eLt">
878+ <rect key="frame" x="53" y="24" width="56" height="18"/>
879 <autoresizingMask key="autoresizingMask"/>
880- <fontDescription key="fontDescription" type="system" pointSize="14"/>
881+ <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
882 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>
883 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
884 </label>
885@@ -86,7 +86,7 @@
886 </objects>
887 <point key="canvasLocation" x="1047" y="-56"/>
888 </scene>
889- <!--Artist View Controller-->
890+ <!--Artist View Controller - Artist-->
891 <scene sceneID="Y3A-G3-aF2">
892 <objects>
893 <viewController storyboardIdentifier="ArtistViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="EDS-Dj-fjS" customClass="ArtistViewController" sceneMemberID="viewController">
894@@ -106,17 +106,17 @@
895 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
896 <autoresizingMask key="autoresizingMask"/>
897 <subviews>
898- <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="G72-IX-jh9">
899- <rect key="frame" x="53" y="2" width="38" height="22"/>
900+ <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Album" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="G72-IX-jh9">
901+ <rect key="frame" x="53" y="1" width="54" height="23"/>
902 <autoresizingMask key="autoresizingMask"/>
903- <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
904+ <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
905 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
906 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
907 </label>
908- <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VIc-Rn-YXm">
909- <rect key="frame" x="53" y="24" width="47" height="18"/>
910+ <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="11 songs" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VIc-Rn-YXm">
911+ <rect key="frame" x="53" y="24" width="57" height="18"/>
912 <autoresizingMask key="autoresizingMask"/>
913- <fontDescription key="fontDescription" type="system" pointSize="14"/>
914+ <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
915 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>
916 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
917 </label>
918@@ -133,29 +133,29 @@
919 </tableViewCell>
920 </prototypes>
921 </tableView>
922- <imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="7CD-jP-Egt">
923- <rect key="frame" x="0.0" y="0.0" width="120" height="120"/>
924+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="default-album-art-200.png" id="7CD-jP-Egt">
925+ <rect key="frame" x="5" y="5" width="110" height="110"/>
926 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
927 </imageView>
928 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="00M-r3-Ezp">
929- <rect key="frame" x="128" y="15" width="192" height="26"/>
930+ <rect key="frame" x="123" y="5" width="172" height="26"/>
931 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
932- <fontDescription key="fontDescription" type="system" pointSize="18"/>
933+ <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
934 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
935 <nil key="highlightedColor"/>
936 <color key="shadowColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
937 </label>
938- <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="6 Albums" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="25K-Tf-YSQ">
939- <rect key="frame" x="128" y="49" width="63" height="21"/>
940+ <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="2 Albums" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="25K-Tf-YSQ">
941+ <rect key="frame" x="123" y="39" width="172" height="21"/>
942 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
943- <fontDescription key="fontDescription" type="system" pointSize="15"/>
944+ <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="15"/>
945 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
946 <color key="highlightedColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
947 </label>
948 </subviews>
949 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
950 </view>
951- <navigationItem key="navigationItem" id="OM6-Kh-nGV">
952+ <navigationItem key="navigationItem" title="Artist" id="OM6-Kh-nGV">
953 <barButtonItem key="rightBarButtonItem" title="Now Playing" id="tdG-O0-4fc">
954 <connections>
955 <segue destination="hRX-A4-gMJ" kind="modal" id="dlu-4W-myG"/>
956@@ -174,7 +174,7 @@
957 </objects>
958 <point key="canvasLocation" x="1515" y="-56"/>
959 </scene>
960- <!--Album View Controller-->
961+ <!--Album View Controller - Album-->
962 <scene sceneID="24B-tK-YA0">
963 <objects>
964 <viewController storyboardIdentifier="AlbumViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="8Ya-S8-w02" customClass="AlbumViewController" sceneMemberID="viewController">
965@@ -214,14 +214,14 @@
966 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="60:00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6u8-pY-Fu0">
967 <rect key="frame" x="242" y="10" width="46" height="21"/>
968 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
969- <fontDescription key="fontDescription" type="system" pointSize="12"/>
970+ <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="12"/>
971 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
972 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
973 </label>
974 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QJk-JO-Hhy">
975- <rect key="frame" x="20" y="10" width="198" height="21"/>
976+ <rect key="frame" x="20" y="8" width="198" height="27"/>
977 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
978- <fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
979+ <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
980 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
981 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
982 </label>
983@@ -250,29 +250,29 @@
984 </tableViewCell>
985 </prototypes>
986 </tableView>
987- <imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="1ZL-7v-l7S">
988- <rect key="frame" x="0.0" y="0.0" width="120" height="120"/>
989+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="default-album-art-200.png" id="1ZL-7v-l7S">
990+ <rect key="frame" x="5" y="5" width="110" height="110"/>
991 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
992 </imageView>
993 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Album" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dwO-gw-nTE">
994- <rect key="frame" x="128" y="15" width="192" height="26"/>
995+ <rect key="frame" x="123" y="5" width="192" height="26"/>
996 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
997- <fontDescription key="fontDescription" type="system" pointSize="18"/>
998+ <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
999 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
1000 <nil key="highlightedColor"/>
1001 <color key="shadowColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
1002 </label>
1003 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="G01-4v-qdB">
1004- <rect key="frame" x="128" y="49" width="63" height="21"/>
1005+ <rect key="frame" x="123" y="39" width="172" height="21"/>
1006 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1007- <fontDescription key="fontDescription" type="system" pointSize="15"/>
1008+ <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="15"/>
1009 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
1010 <color key="highlightedColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
1011 </label>
1012 </subviews>
1013 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
1014 </view>
1015- <navigationItem key="navigationItem" id="a61-1v-dxt">
1016+ <navigationItem key="navigationItem" title="Album" id="a61-1v-dxt">
1017 <barButtonItem key="rightBarButtonItem" title="Now Playing" id="wGH-uZ-TGk">
1018 <connections>
1019 <segue destination="hRX-A4-gMJ" kind="modal" identifier="NowPlayingSegue" id="YJd-qV-EXm"/>
1020@@ -395,7 +395,7 @@
1021 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="0:00" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" id="pNU-Jr-yiV">
1022 <rect key="frame" x="12" y="31" width="42" height="15"/>
1023 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1024- <fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="14"/>
1025+ <fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
1026 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
1027 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
1028 <color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
1029@@ -403,7 +403,7 @@
1030 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="10:00" lineBreakMode="tailTruncation" minimumFontSize="10" id="fsV-6P-1Kb">
1031 <rect key="frame" x="266" y="31" width="42" height="15"/>
1032 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1033- <fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="14"/>
1034+ <fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
1035 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
1036 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
1037 <color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
1038@@ -411,7 +411,7 @@
1039 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="3 of 100" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" id="KsY-jo-7df">
1040 <rect key="frame" x="12" y="5" width="296" height="19"/>
1041 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1042- <fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="14"/>
1043+ <fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
1044 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
1045 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
1046 <color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
1047@@ -528,17 +528,17 @@
1048 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
1049 <autoresizingMask key="autoresizingMask"/>
1050 <subviews>
1051- <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="lHq-bS-Zm9">
1052- <rect key="frame" x="53" y="2" width="38" height="22"/>
1053+ <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Album" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="lHq-bS-Zm9">
1054+ <rect key="frame" x="53" y="1" width="54" height="23"/>
1055 <autoresizingMask key="autoresizingMask"/>
1056- <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
1057+ <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
1058 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
1059 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
1060 </label>
1061- <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="03B-YT-E7s">
1062- <rect key="frame" x="53" y="24" width="47" height="18"/>
1063+ <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="03B-YT-E7s">
1064+ <rect key="frame" x="53" y="24" width="31" height="18"/>
1065 <autoresizingMask key="autoresizingMask"/>
1066- <fontDescription key="fontDescription" type="system" pointSize="14"/>
1067+ <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
1068 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>
1069 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
1070 </label>
1071@@ -579,41 +579,35 @@
1072 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1073 <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
1074 <prototypes>
1075- <tableViewCell contentMode="scaleToFill" selectionStyle="gray" indentationWidth="10" reuseIdentifier="Cell" id="EzM-5L-53C" customClass="SongCell">
1076+ <tableViewCell contentMode="scaleToFill" selectionStyle="gray" indentationWidth="10" reuseIdentifier="Cell" textLabel="FGV-Wb-IfK" detailTextLabel="i6c-1Q-zdc" imageView="eTE-GK-6eP" style="IBUITableViewCellStyleSubtitle" id="EzM-5L-53C" customClass="SongListCell">
1077 <rect key="frame" x="0.0" y="22" width="320" height="44"/>
1078 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1079 <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
1080 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
1081 <autoresizingMask key="autoresizingMask"/>
1082 <subviews>
1083- <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="URM-e4-2yc">
1084- <rect key="frame" x="20" y="11" width="31" height="21"/>
1085- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1086- <fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
1087- <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
1088- <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
1089- </label>
1090- <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="60:00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="vws-t4-lyH">
1091- <rect key="frame" x="254" y="11" width="46" height="21"/>
1092- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1093- <fontDescription key="fontDescription" type="system" pointSize="12"/>
1094- <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
1095- <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
1096- </label>
1097- <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="cHc-6o-Le1">
1098- <rect key="frame" x="59" y="11" width="198" height="21"/>
1099- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1100- <fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
1101- <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
1102- <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
1103- </label>
1104+ <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="FGV-Wb-IfK">
1105+ <rect key="frame" x="53" y="1" width="36" height="23"/>
1106+ <autoresizingMask key="autoresizingMask"/>
1107+ <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
1108+ <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
1109+ <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
1110+ </label>
1111+ <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Subtitle" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="i6c-1Q-zdc">
1112+ <rect key="frame" x="53" y="24" width="46" height="18"/>
1113+ <autoresizingMask key="autoresizingMask"/>
1114+ <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
1115+ <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>
1116+ <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
1117+ </label>
1118+ <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="default-album-art-120.png" id="eTE-GK-6eP">
1119+ <rect key="frame" x="0.0" y="0.0" width="43" height="43"/>
1120+ <autoresizingMask key="autoresizingMask"/>
1121+ </imageView>
1122 </subviews>
1123 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
1124 </view>
1125 <connections>
1126- <outlet property="clock" destination="vws-t4-lyH" id="Fb3-aG-7Ym"/>
1127- <outlet property="title" destination="cHc-6o-Le1" id="XJD-HL-AWU"/>
1128- <outlet property="track" destination="URM-e4-2yc" id="CNs-EV-tVd"/>
1129 <segue destination="hRX-A4-gMJ" kind="modal" id="ebj-wD-yft"/>
1130 </connections>
1131 </tableViewCell>
1132@@ -960,24 +954,22 @@
1133 </scene>
1134 </scenes>
1135 <resources>
1136- <image name="03-loopback.png" width="32" height="22"/>
1137- <image name="05-shuffle.png" width="28" height="20"/>
1138- <image name="albums.png" width="30" height="30"/>
1139- <image name="artists.png" width="30" height="30"/>
1140- <image name="default-album-art-120.png" width="60" height="60"/>
1141- <image name="default-album-art-640.png" width="320" height="320"/>
1142- <image name="download-grey.png" width="28" height="28"/>
1143- <image name="download.png" width="28" height="28"/>
1144- <image name="grabber.png" width="22" height="44"/>
1145- <image name="player_overlay_bg.png" width="320" height="100"/>
1146- <image name="playlists.png" width="30" height="30"/>
1147- <image name="settings.png" width="30" height="30"/>
1148- <image name="songs.png" width="30" height="30"/>
1149+ <image name="03-loopback.png" width="16" height="16"/>
1150+ <image name="05-shuffle.png" width="16" height="16"/>
1151+ <image name="albums.png" width="16" height="16"/>
1152+ <image name="artists.png" width="16" height="16"/>
1153+ <image name="default-album-art-120.png" width="16" height="16"/>
1154+ <image name="default-album-art-200.png" width="16" height="16"/>
1155+ <image name="default-album-art-640.png" width="16" height="16"/>
1156+ <image name="download-grey.png" width="16" height="16"/>
1157+ <image name="download.png" width="16" height="16"/>
1158+ <image name="grabber.png" width="16" height="16"/>
1159+ <image name="player_overlay_bg.png" width="16" height="16"/>
1160+ <image name="playlists.png" width="16" height="16"/>
1161+ <image name="settings.png" width="16" height="16"/>
1162+ <image name="songs.png" width="16" height="16"/>
1163 </resources>
1164 <classes>
1165- <class className="AlbumCell" superclassName="UITableViewCell">
1166- <source key="sourceIdentifier" type="project" relativePath="./Classes/AlbumCell.h"/>
1167- </class>
1168 <class className="AlbumViewController" superclassName="UIViewController">
1169 <source key="sourceIdentifier" type="project" relativePath="./Classes/AlbumViewController.h"/>
1170 <relationships>
1171@@ -988,69 +980,16 @@
1172 <relationship kind="outlet" name="tableView" candidateClass="UITableView"/>
1173 </relationships>
1174 </class>
1175- <class className="AlbumsViewController" superclassName="UOIndexedViewController">
1176- <source key="sourceIdentifier" type="project" relativePath="./Classes/AlbumsViewController.h"/>
1177- </class>
1178- <class className="ArtistCell" superclassName="UITableViewCell">
1179- <source key="sourceIdentifier" type="project" relativePath="./Classes/ArtistCell.h"/>
1180- </class>
1181- <class className="ArtistViewController" superclassName="UIViewController">
1182+ <class className="ArtistViewController">
1183 <source key="sourceIdentifier" type="project" relativePath="./Classes/ArtistViewController.h"/>
1184 <relationships>
1185 <relationship kind="outlet" name="albumArt" candidateClass="UIImageView"/>
1186 <relationship kind="outlet" name="artistDescription" candidateClass="UILabel"/>
1187 <relationship kind="outlet" name="artistName" candidateClass="UILabel"/>
1188- <relationship kind="outlet" name="nowPlayingButton" candidateClass="UIBarButtonItem"/>
1189 <relationship kind="outlet" name="tableView" candidateClass="UITableView"/>
1190 </relationships>
1191 </class>
1192- <class className="ArtistsViewController" superclassName="UOIndexedViewController">
1193- <source key="sourceIdentifier" type="project" relativePath="./Classes/ArtistsViewController.h"/>
1194- </class>
1195- <class className="PlayerHeaderView" superclassName="UIView">
1196- <source key="sourceIdentifier" type="project" relativePath="./Classes/PlayerHeaderView.h"/>
1197- <relationships>
1198- <relationship kind="outlet" name="album" candidateClass="UILabel"/>
1199- <relationship kind="outlet" name="artist" candidateClass="UILabel"/>
1200- <relationship kind="outlet" name="title" candidateClass="UILabel"/>
1201- </relationships>
1202- </class>
1203- <class className="PlayerViewController" superclassName="UIViewController">
1204- <source key="sourceIdentifier" type="project" relativePath="./Classes/PlayerViewController.h"/>
1205- <relationships>
1206- <relationship kind="action" name="changeRepeat:"/>
1207- <relationship kind="action" name="changeShuffle:"/>
1208- <relationship kind="action" name="hide:"/>
1209- <relationship kind="action" name="next:"/>
1210- <relationship kind="action" name="pause:"/>
1211- <relationship kind="action" name="play:"/>
1212- <relationship kind="action" name="previous:"/>
1213- <relationship kind="action" name="toggleExtendedControls:"/>
1214- <relationship kind="outlet" name="albumart" candidateClass="UIButton"/>
1215- <relationship kind="outlet" name="artistLabel" candidateClass="UILabel"/>
1216- <relationship kind="outlet" name="controlBar" candidateClass="UIToolbar"/>
1217- <relationship kind="outlet" name="elapsedTime" candidateClass="UILabel"/>
1218- <relationship kind="outlet" name="extendedControlView" candidateClass="UIView"/>
1219- <relationship kind="outlet" name="nextButton" candidateClass="UIBarButtonItem"/>
1220- <relationship kind="outlet" name="pauseButton" candidateClass="UIBarButtonItem"/>
1221- <relationship kind="outlet" name="playButton" candidateClass="UIBarButtonItem"/>
1222- <relationship kind="outlet" name="playlistInfo" candidateClass="UILabel"/>
1223- <relationship kind="outlet" name="previousButton" candidateClass="UIBarButtonItem"/>
1224- <relationship kind="outlet" name="progressView" candidateClass="UISlider"/>
1225- <relationship kind="outlet" name="reflectionImage" candidateClass="UIImageView"/>
1226- <relationship kind="outlet" name="repeatButton" candidateClass="UIButton"/>
1227- <relationship kind="outlet" name="shuffleButton" candidateClass="UIButton"/>
1228- <relationship kind="outlet" name="titleLabel" candidateClass="UILabel"/>
1229- <relationship kind="outlet" name="totalTime" candidateClass="UILabel"/>
1230- </relationships>
1231- </class>
1232- <class className="PlaylistCell" superclassName="UITableViewCell">
1233- <source key="sourceIdentifier" type="project" relativePath="./Classes/PlaylistCell.h"/>
1234- </class>
1235- <class className="PlaylistsViewController" superclassName="UOIndexedViewController">
1236- <source key="sourceIdentifier" type="project" relativePath="./Classes/PlaylistsViewController.h"/>
1237- </class>
1238- <class className="SettingsAuthenticationViewController" superclassName="UIViewController">
1239+ <class className="SettingsAuthenticationViewController">
1240 <source key="sourceIdentifier" type="project" relativePath="./Classes/SettingsAuthenticationViewController.h"/>
1241 <relationships>
1242 <relationship kind="action" name="authenticate:"/>
1243@@ -1060,7 +999,7 @@
1244 <relationship kind="outlet" name="usernameField" candidateClass="UITextField"/>
1245 </relationships>
1246 </class>
1247- <class className="SettingsViewController" superclassName="UITableViewController">
1248+ <class className="SettingsViewController">
1249 <source key="sourceIdentifier" type="project" relativePath="./Classes/SettingsViewController.h"/>
1250 <relationships>
1251 <relationship kind="action" name="deleteCaches:"/>
1252@@ -1071,27 +1010,6 @@
1253 <relationship kind="outlet" name="versionNumber" candidateClass="UILabel"/>
1254 </relationships>
1255 </class>
1256- <class className="SongCell" superclassName="UITableViewCell">
1257- <source key="sourceIdentifier" type="project" relativePath="./Classes/SongCell.h"/>
1258- <relationships>
1259- <relationship kind="outlet" name="cacheImageIndicator" candidateClass="UIImageView"/>
1260- <relationship kind="outlet" name="cacheIndicator" candidateClass="UIView"/>
1261- <relationship kind="outlet" name="clock" candidateClass="UILabel"/>
1262- <relationship kind="outlet" name="handle" candidateClass="UIImageView"/>
1263- <relationship kind="outlet" name="title" candidateClass="UILabel"/>
1264- <relationship kind="outlet" name="topView" candidateClass="UIView"/>
1265- <relationship kind="outlet" name="track" candidateClass="UILabel"/>
1266- </relationships>
1267- </class>
1268- <class className="SongsViewController" superclassName="UOIndexedViewController">
1269- <source key="sourceIdentifier" type="project" relativePath="./Classes/SongsViewController.h"/>
1270- </class>
1271- <class className="UOIndexedViewController" superclassName="UITableViewController">
1272- <source key="sourceIdentifier" type="project" relativePath="./Classes/UOIndexedViewController.h"/>
1273- <relationships>
1274- <relationship kind="outlet" name="nowPlayingButton" candidateClass="UIBarButtonItem"/>
1275- </relationships>
1276- </class>
1277 </classes>
1278 <simulatedMetricsContainer key="defaultSimulatedMetrics">
1279 <simulatedStatusBarMetrics key="statusBar"/>
1280@@ -1100,6 +1018,6 @@
1281 </simulatedMetricsContainer>
1282 <inferredMetricsTieBreakers>
1283 <segue reference="jMd-4K-zoU"/>
1284- <segue reference="e8X-Xp-0ra"/>
1285+ <segue reference="ebj-wD-yft"/>
1286 </inferredMetricsTieBreakers>
1287 </document>
1288\ No newline at end of file
1289
1290=== modified file 'Music/Utilities/UOPlayer.h'
1291--- Music/Utilities/UOPlayer.h 2013-02-11 04:30:25 +0000
1292+++ Music/Utilities/UOPlayer.h 2013-02-11 04:30:25 +0000
1293@@ -45,4 +45,6 @@
1294 @property (nonatomic) BOOL shuffle;
1295 @property (nonatomic) RepeatState repeat;
1296
1297+- (double)streamerProgress;
1298+
1299 @end
1300
1301=== modified file 'Music/Utilities/UOPlayer.m'
1302--- Music/Utilities/UOPlayer.m 2013-02-11 04:30:25 +0000
1303+++ Music/Utilities/UOPlayer.m 2013-02-11 04:30:25 +0000
1304@@ -106,7 +106,7 @@
1305
1306 #pragma mark - Player state
1307
1308-- (double)progress {
1309+- (double)streamerProgress {
1310 return streamer.progress;
1311 }
1312
1313
1314=== modified file 'Music/View Controllers/AlbumViewController.h'
1315--- Music/View Controllers/AlbumViewController.h 2013-01-24 15:46:16 +0000
1316+++ Music/View Controllers/AlbumViewController.h 2013-02-11 04:30:25 +0000
1317@@ -12,6 +12,7 @@
1318
1319 @interface AlbumViewController : UIViewController
1320 @property (nonatomic, retain) NSString *albumId;
1321+@property (nonatomic, retain) NSString *artistId;
1322
1323 @property (strong, nonatomic) IBOutlet UIBarButtonItem *nowPlayingButton;
1324 @end
1325
1326=== modified file 'Music/View Controllers/AlbumViewController.m'
1327--- Music/View Controllers/AlbumViewController.m 2013-02-11 04:30:25 +0000
1328+++ Music/View Controllers/AlbumViewController.m 2013-02-11 04:30:25 +0000
1329@@ -20,6 +20,7 @@
1330 NSArray *tableData;
1331
1332 Album *album;
1333+ Artist *artist;
1334 }
1335 @property (weak, nonatomic) IBOutlet UIImageView *albumArt;
1336 @property (weak, nonatomic) IBOutlet UILabel *albumTitle;
1337@@ -30,6 +31,7 @@
1338
1339 @implementation AlbumViewController
1340 @synthesize albumId = _albumId;
1341+@synthesize artistId = _artistId;
1342
1343 - (void)viewDidLoad {
1344 [self.tableView setDataSource:self];
1345@@ -58,7 +60,11 @@
1346
1347 if ([album.songs count]) {
1348 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"track" ascending:YES];
1349- tableData = [album.songs sortedArrayUsingDescriptors:@[sortDescriptor]];
1350+ if (self.artistId) {
1351+ tableData = [[album songsForArtistId:self.artistId] sortedArrayUsingDescriptors:@[sortDescriptor]];
1352+ } else {
1353+ tableData = [album.songs sortedArrayUsingDescriptors:@[sortDescriptor]];
1354+ }
1355 [self.tableView reloadData];
1356 } else {
1357 [[UOWebServiceController controller] updateSongs:self];
1358@@ -116,7 +122,11 @@
1359 [managedObjectContext refreshObject:album mergeChanges:NO];
1360
1361 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"track" ascending:YES];
1362- tableData = [album.songs sortedArrayUsingDescriptors:@[sortDescriptor]];
1363+ if (self.artistId) {
1364+ tableData = [[album songsForArtistId:self.artistId] sortedArrayUsingDescriptors:@[sortDescriptor]];
1365+ } else {
1366+ tableData = [album.songs sortedArrayUsingDescriptors:@[sortDescriptor]];
1367+ }
1368
1369 [self.tableView reloadData];
1370 }
1371
1372=== modified file 'Music/View Controllers/AlbumsViewController.m'
1373--- Music/View Controllers/AlbumsViewController.m 2013-01-24 16:37:09 +0000
1374+++ Music/View Controllers/AlbumsViewController.m 2013-02-11 04:30:25 +0000
1375@@ -46,6 +46,10 @@
1376 [[UOWebServiceController controller] updateAlbums:self];
1377 }
1378
1379+- (void)refresh {
1380+ [[UOWebServiceController controller] updateAlbums:self clearCache:YES];
1381+}
1382+
1383 #pragma mark - Storyboard methods
1384
1385 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
1386@@ -55,6 +59,7 @@
1387 AlbumViewController *albumViewController = [segue destinationViewController];
1388 Album *album = ((AlbumCell *)sender).album;
1389 albumViewController.albumId = album.albumId;
1390+ albumViewController.artistId = nil;
1391 }
1392
1393 @end
1394
1395=== modified file 'Music/View Controllers/ArtistViewController.m'
1396--- Music/View Controllers/ArtistViewController.m 2013-02-11 04:30:25 +0000
1397+++ Music/View Controllers/ArtistViewController.m 2013-02-11 04:30:25 +0000
1398@@ -59,11 +59,7 @@
1399 self.navigationItem.rightBarButtonItem = nil;
1400 }
1401
1402- NSString *pluralizedNoun = @"albums";
1403- if ([artist.albums count] == 1) {
1404- pluralizedNoun = @"album";
1405- }
1406- [_artistDescription setText:[NSString stringWithFormat:@"%d %@", [artist.albums count], pluralizedNoun]];
1407+ [self setAlbumCount];
1408 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"year" ascending:NO];
1409 tableData = [artist.albums sortedArrayUsingDescriptors:@[sortDescriptor]];
1410 [self.tableView reloadData];
1411@@ -98,6 +94,7 @@
1412 AlbumViewController *albumViewController = [segue destinationViewController];
1413 Album *album = ((AlbumCell *)sender).album;
1414 albumViewController.albumId = album.albumId;
1415+ albumViewController.artistId = self.artistId;
1416 }
1417
1418 #pragma mark - UOWebServiceDelegate methods
1419@@ -110,6 +107,7 @@
1420 tableData = [artist.albums sortedArrayUsingDescriptors:@[sortDescriptor]];
1421
1422 [self.tableView reloadData];
1423+ [self setAlbumCount];
1424 }
1425
1426 - (void)webserviceUpdateError:(NSError *)error {
1427@@ -139,4 +137,12 @@
1428 artist = [results objectAtIndex:0];
1429 }
1430
1431+- (void)setAlbumCount {
1432+ NSString *pluralizedNoun = @"albums";
1433+ if ([artist.albums count] == 1) {
1434+ pluralizedNoun = @"album";
1435+ }
1436+ [_artistDescription setText:[NSString stringWithFormat:@"%d %@", [artist.albums count], pluralizedNoun]];
1437+}
1438+
1439 @end
1440
1441=== modified file 'Music/View Controllers/ArtistsViewController.m'
1442--- Music/View Controllers/ArtistsViewController.m 2013-01-24 15:46:16 +0000
1443+++ Music/View Controllers/ArtistsViewController.m 2013-02-11 04:30:25 +0000
1444@@ -49,6 +49,10 @@
1445 [[UOWebServiceController controller] updateArtists:self];
1446 }
1447
1448+- (void)refresh {
1449+ [[UOWebServiceController controller] updateArtists:self clearCache:YES];
1450+}
1451+
1452 #pragma mark - Storyboard methods
1453
1454 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
1455
1456=== modified file 'Music/View Controllers/PlayerViewController.m'
1457--- Music/View Controllers/PlayerViewController.m 2013-02-11 04:30:25 +0000
1458+++ Music/View Controllers/PlayerViewController.m 2013-02-11 04:30:25 +0000
1459@@ -185,8 +185,8 @@
1460 }
1461
1462 - (void)heartbeat:(NSTimer*)timer {
1463- NSInteger progress = (NSInteger)[[UOPlayer player] progress];
1464- self.elapsedTime.text = [NSString clockStringFromSeconds:progress];
1465+ double progress = [[UOPlayer player] streamerProgress];
1466+ self.elapsedTime.text = [NSString clockStringFromSeconds:(int)progress];
1467 self.progressView.value = progress;
1468 }
1469
1470
1471=== modified file 'Music/View Controllers/PlaylistsViewController.m'
1472--- Music/View Controllers/PlaylistsViewController.m 2013-01-22 19:50:43 +0000
1473+++ Music/View Controllers/PlaylistsViewController.m 2013-02-11 04:30:25 +0000
1474@@ -45,4 +45,8 @@
1475 [[UOWebServiceController controller] updatePlaylists:self];
1476 }
1477
1478+- (void)refresh {
1479+ [[UOWebServiceController controller] updatePlaylists:self clearCache:YES];
1480+}
1481+
1482 @end
1483
1484=== modified file 'Music/View Controllers/SongsViewController.m'
1485--- Music/View Controllers/SongsViewController.m 2013-02-11 04:30:25 +0000
1486+++ Music/View Controllers/SongsViewController.m 2013-02-11 04:30:25 +0000
1487@@ -9,7 +9,7 @@
1488 #import "SongsViewController.h"
1489 #import "UONetworkStatusCoordinator.h"
1490 #import "Song.h"
1491-#import "SongCell.h"
1492+#import "SongListCell.h"
1493 #import "NSString+Extras.h"
1494 #import "UOWebServiceController.h"
1495 #import "UOPlayer.h"
1496@@ -37,23 +37,27 @@
1497 }
1498
1499 - (void)configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath {
1500- SongCell *songCell = (SongCell *)cell;
1501+ SongListCell *songCell = (SongListCell *)cell;
1502 Song *song = [self.fetchedResultsController objectAtIndexPath:indexPath];
1503- songCell.showArt = YES;
1504 [songCell setSong:song];
1505 }
1506
1507-- (void)viewWillAppear:(BOOL)animated {
1508- [super viewWillAppear:animated];
1509+- (void)update {
1510 [[UOWebServiceController controller] updateSongs:self];
1511 }
1512
1513+- (void)refresh {
1514+ [[UOWebServiceController controller] updateSongs:self clearCache:YES];
1515+}
1516+
1517+#pragma mark - UIStoryboard methods
1518+
1519 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
1520 NSArray *songs = [[self.fetchedResultsController fetchedObjects] copy];
1521 if (![sender respondsToSelector:@selector(song)]) {
1522 return; // Hit 'Now Playing'
1523 }
1524- Song *song = ((SongCell *)sender).song;
1525+ Song *song = ((SongListCell *)sender).song;
1526 NSUInteger index = [songs indexOfObject:song];
1527
1528 UOPlayer *player = [UOPlayer player];
1529
1530=== modified file 'Music/View Controllers/UOIndexedViewController.h'
1531--- Music/View Controllers/UOIndexedViewController.h 2013-01-24 15:46:16 +0000
1532+++ Music/View Controllers/UOIndexedViewController.h 2013-02-11 04:30:25 +0000
1533@@ -23,6 +23,7 @@
1534 @property (readonly, nonatomic) NSString *lastUpdatedKey;
1535 @property (readonly, nonatomic) NSArray *sortDescriptors;
1536 - (void)update;
1537+- (void)refresh;
1538 - (void)configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath;
1539
1540 - (NSFetchedResultsController *)fetchedResultsController;
1541
1542=== modified file 'Music/View Controllers/UOIndexedViewController.m'
1543--- Music/View Controllers/UOIndexedViewController.m 2013-02-11 04:30:25 +0000
1544+++ Music/View Controllers/UOIndexedViewController.m 2013-02-11 04:30:25 +0000
1545@@ -10,11 +10,12 @@
1546 #import <RestKit/RestKit.h>
1547 #import "UOAppDelegate.h"
1548 #import "UOPlayer.h"
1549+#import "SSPullToRefresh.h"
1550
1551-@interface UOIndexedViewController () {
1552+@interface UOIndexedViewController () <SSPullToRefreshViewDelegate> {
1553 NSFetchedResultsController *_fetchedResultsController;
1554 }
1555-
1556+@property (nonatomic, strong) SSPullToRefreshView *pullToRefreshView;
1557 @end
1558
1559 @implementation UOIndexedViewController
1560@@ -48,13 +49,24 @@
1561 - (void)update {
1562 }
1563
1564+- (void)refresh {
1565+}
1566+
1567 #pragma mark - UIViewController lifecycle
1568
1569 - (void)viewDidLoad {
1570 [super viewDidLoad];
1571+
1572+ self.pullToRefreshView = [[SSPullToRefreshView alloc] initWithScrollView:self.tableView delegate:self];
1573+ self.pullToRefreshView.contentView = [[SSPullToRefreshSimpleContentView alloc] initWithFrame:CGRectZero];
1574 groupedTableData = [NSMutableDictionary dictionary];
1575 }
1576
1577+- (void)viewDidUnload {
1578+ [super viewDidUnload];
1579+ self.pullToRefreshView = nil;
1580+}
1581+
1582 - (void)viewWillAppear:(BOOL)animated {
1583 [super viewWillAppear:animated];
1584 [self update];
1585@@ -66,12 +78,6 @@
1586 }
1587 }
1588
1589-- (void)didReceiveMemoryWarning
1590-{
1591- [super didReceiveMemoryWarning];
1592- // Dispose of any resources that can be recreated.
1593-}
1594-
1595 #pragma mark - Table view data source
1596
1597 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
1598@@ -114,25 +120,6 @@
1599 NSLog(@"Error: %@", [error localizedDescription]);
1600 }
1601
1602-#pragma mark - RKObjectLoaderDelegate methods
1603-/*
1604-- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error {
1605- [UONetworkStatusCoordinator removeNetworkActivity];
1606- if ([error code] == 2) { // Network is down.
1607- return;
1608- }
1609- NSLog(@"Error: %@", [error localizedDescription]);
1610- [[NSUserDefaults standardUserDefaults] setObject:nil forKey:[self lastUpdatedKey]];
1611-}
1612-
1613-- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
1614- [UONetworkStatusCoordinator removeNetworkActivity];
1615- _fetchedResultsController = nil;
1616- [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:[self lastUpdatedKey]];
1617- [self.tableView reloadData];
1618-}
1619- */
1620-
1621 #pragma mark - Fetched results controller
1622
1623 - (NSFetchedResultsController *)fetchedResultsController {
1624@@ -166,4 +153,13 @@
1625 [self.tableView reloadData];
1626 }
1627
1628+#pragma mark - SSPullToRefreshViewDelegate methods
1629+
1630+- (void)pullToRefreshViewDidStartLoading:(SSPullToRefreshView *)view {
1631+ [self.pullToRefreshView startLoading];
1632+ [self refresh];
1633+ [self.pullToRefreshView finishLoading];
1634+}
1635+
1636+
1637 @end
1638
1639=== modified file 'Music/Views/Table Cells/ArtistCell.m'
1640--- Music/Views/Table Cells/ArtistCell.m 2013-01-28 00:40:22 +0000
1641+++ Music/Views/Table Cells/ArtistCell.m 2013-02-11 04:30:25 +0000
1642@@ -32,6 +32,8 @@
1643 pluralizedNoun = @"album";
1644 }
1645 [self.detailTextLabel setText:[NSString stringWithFormat:@"%d %@", [artist.albums count], pluralizedNoun]];
1646+ } else {
1647+ [self.detailTextLabel setText:@" "];
1648 }
1649 }
1650
1651
1652=== added file 'Music/Views/Table Cells/SongListCell.h'
1653--- Music/Views/Table Cells/SongListCell.h 1970-01-01 00:00:00 +0000
1654+++ Music/Views/Table Cells/SongListCell.h 2013-02-11 04:30:25 +0000
1655@@ -0,0 +1,15 @@
1656+//
1657+// SongListCell.h
1658+// U1Music
1659+//
1660+// Created by Paul Hummer on 2/6/13.
1661+// Copyright (c) 2013 Canonical. All rights reserved.
1662+//
1663+
1664+#import <UIKit/UIKit.h>
1665+
1666+@class Song;
1667+
1668+@interface SongListCell : UITableViewCell
1669+@property (nonatomic, retain) Song *song;
1670+@end
1671
1672=== added file 'Music/Views/Table Cells/SongListCell.m'
1673--- Music/Views/Table Cells/SongListCell.m 1970-01-01 00:00:00 +0000
1674+++ Music/Views/Table Cells/SongListCell.m 2013-02-11 04:30:25 +0000
1675@@ -0,0 +1,34 @@
1676+//
1677+// SongListCell.m
1678+// U1Music
1679+//
1680+// Created by Paul Hummer on 2/6/13.
1681+// Copyright (c) 2013 Canonical. All rights reserved.
1682+//
1683+
1684+#import "SongListCell.h"
1685+#import "UODownloader.h"
1686+#import "Song.h"
1687+#import "Artist.h"
1688+
1689+@implementation SongListCell
1690+@synthesize song;
1691+
1692+- (void)setSong:(Song *)_song {
1693+ song = _song;
1694+
1695+ if (!song.art) {
1696+ [self.imageView setImage:[UIImage imageNamed:@"default-album-art-120.png"]];
1697+ [[UODownloader downloader] downloadArt:song.artUrl toPath:song.artPath completionBlock:^(UIImage *image){
1698+ [self.imageView setImage:image];
1699+ [self setNeedsDisplay];
1700+ }];
1701+ } else {
1702+ [self.imageView setImage:song.art];
1703+ }
1704+
1705+ [self.textLabel setText:song.title];
1706+ [self.detailTextLabel setText:song.artist.name];
1707+}
1708+
1709+@end
1710
1711=== modified file 'U1Music.xcodeproj/project.pbxproj'
1712--- U1Music.xcodeproj/project.pbxproj 2013-02-11 04:30:25 +0000
1713+++ U1Music.xcodeproj/project.pbxproj 2013-02-11 04:30:25 +0000
1714@@ -28,6 +28,10 @@
1715 523B3CFA15B73BA0004394F4 /* download-grey@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF615B73BA0004394F4 /* download-grey@2x.png */; };
1716 523B3CFB15B73BA0004394F4 /* download.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF715B73BA0004394F4 /* download.png */; };
1717 523B3CFC15B73BA0004394F4 /* download@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF815B73BA0004394F4 /* download@2x.png */; };
1718+ 5257414B16C37A0A00530CCC /* SongListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257414A16C37A0A00530CCC /* SongListCell.m */; };
1719+ 5257415416C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257414F16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m */; };
1720+ 5257415516C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257415116C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m */; };
1721+ 5257415616C37E1A00530CCC /* SSPullToRefreshView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257415316C37E1A00530CCC /* SSPullToRefreshView.m */; };
1722 5257417716C5CC5D00530CCC /* NSString+UbuntuOne.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257417616C5CC5D00530CCC /* NSString+UbuntuOne.m */; };
1723 5257417A16C5CDA900530CCC /* UIImageView+UbuntuOne.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257417916C5CDA900530CCC /* UIImageView+UbuntuOne.m */; };
1724 5257416D16C5653100530CCC /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5257416C16C5653100530CCC /* Crashlytics.framework */; };
1725@@ -274,6 +278,15 @@
1726 523B3CF615B73BA0004394F4 /* download-grey@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "download-grey@2x.png"; sourceTree = "<group>"; };
1727 523B3CF715B73BA0004394F4 /* download.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = download.png; sourceTree = "<group>"; };
1728 523B3CF815B73BA0004394F4 /* download@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "download@2x.png"; sourceTree = "<group>"; };
1729+ 5257414916C37A0A00530CCC /* SongListCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SongListCell.h; sourceTree = "<group>"; };
1730+ 5257414A16C37A0A00530CCC /* SongListCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SongListCell.m; sourceTree = "<group>"; };
1731+ 5257414D16C37E1A00530CCC /* SSPullToRefresh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSPullToRefresh.h; sourceTree = "<group>"; };
1732+ 5257414E16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSPullToRefreshDefaultContentView.h; sourceTree = "<group>"; };
1733+ 5257414F16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSPullToRefreshDefaultContentView.m; sourceTree = "<group>"; };
1734+ 5257415016C37E1A00530CCC /* SSPullToRefreshSimpleContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSPullToRefreshSimpleContentView.h; sourceTree = "<group>"; };
1735+ 5257415116C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSPullToRefreshSimpleContentView.m; sourceTree = "<group>"; };
1736+ 5257415216C37E1A00530CCC /* SSPullToRefreshView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSPullToRefreshView.h; sourceTree = "<group>"; };
1737+ 5257415316C37E1A00530CCC /* SSPullToRefreshView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSPullToRefreshView.m; sourceTree = "<group>"; };
1738 5257417516C5CC5D00530CCC /* NSString+UbuntuOne.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+UbuntuOne.h"; path = "Categories/NSString+UbuntuOne.h"; sourceTree = "<group>"; };
1739 5257417616C5CC5D00530CCC /* NSString+UbuntuOne.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+UbuntuOne.m"; path = "Categories/NSString+UbuntuOne.m"; sourceTree = "<group>"; };
1740 5257417816C5CDA900530CCC /* UIImageView+UbuntuOne.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImageView+UbuntuOne.h"; path = "Categories/UIImageView+UbuntuOne.h"; sourceTree = "<group>"; };
1741@@ -659,6 +672,7 @@
1742 29B97317FDCFA39411CA2CEA /* Resources */,
1743 964FA39013CA5BE60018A65B /* Dependencies */,
1744 29B97323FDCFA39411CA2CEA /* Frameworks */,
1745+ 5279764815F00B2600F8435F /* libz.dylib */,
1746 19C28FACFE9D520D11CA2CBB /* Products */,
1747 );
1748 name = CustomTemplate;
1749@@ -713,6 +727,21 @@
1750 name = Categories;
1751 sourceTree = "<group>";
1752 };
1753+ 5257414C16C37E1A00530CCC /* SSPullToRefresh */ = {
1754+ isa = PBXGroup;
1755+ children = (
1756+ 5257414D16C37E1A00530CCC /* SSPullToRefresh.h */,
1757+ 5257414E16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.h */,
1758+ 5257414F16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m */,
1759+ 5257415016C37E1A00530CCC /* SSPullToRefreshSimpleContentView.h */,
1760+ 5257415116C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m */,
1761+ 5257415216C37E1A00530CCC /* SSPullToRefreshView.h */,
1762+ 5257415316C37E1A00530CCC /* SSPullToRefreshView.m */,
1763+ );
1764+ name = SSPullToRefresh;
1765+ path = Dependencies/SSPullToRefresh;
1766+ sourceTree = "<group>";
1767+ };
1768 5268508616AE516B001F65A6 /* Products */ = {
1769 isa = PBXGroup;
1770 children = (
1771@@ -801,6 +830,8 @@
1772 526850DB16AEFA7D001F65A6 /* PlaylistCell.m */,
1773 526850DC16AEFA7D001F65A6 /* SongCell.h */,
1774 526850DD16AEFA7D001F65A6 /* SongCell.m */,
1775+ 5257414916C37A0A00530CCC /* SongListCell.h */,
1776+ 5257414A16C37A0A00530CCC /* SongListCell.m */,
1777 );
1778 path = "Table Cells";
1779 sourceTree = "<group>";
1780@@ -1268,8 +1299,8 @@
1781 964FA39013CA5BE60018A65B /* Dependencies */ = {
1782 isa = PBXGroup;
1783 children = (
1784+ 5257414C16C37E1A00530CCC /* SSPullToRefresh */,
1785 93BC520A124C187700B7587C /* SynthesizeSingleton.h */,
1786- 5279764815F00B2600F8435F /* libz.dylib */,
1787 91328278144E07EA00395F40 /* TestFlight SDK */,
1788 );
1789 name = Dependencies;
1790@@ -1631,6 +1662,10 @@
1791 522B24EA16B4BC1E0084B023 /* UODownloadPanGestureRecognizer.m in Sources */,
1792 520BBF2416B51F2A00307F32 /* UODownloader.m in Sources */,
1793 5295FD2416B7559F00A07440 /* UOPlayer.m in Sources */,
1794+ 5257414B16C37A0A00530CCC /* SongListCell.m in Sources */,
1795+ 5257415416C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m in Sources */,
1796+ 5257415516C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m in Sources */,
1797+ 5257415616C37E1A00530CCC /* SSPullToRefreshView.m in Sources */,
1798 5257417716C5CC5D00530CCC /* NSString+UbuntuOne.m in Sources */,
1799 5257417A16C5CDA900530CCC /* UIImageView+UbuntuOne.m in Sources */,
1800 );

Subscribers

People subscribed via source and target branches