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
=== added directory 'Dependencies/SSPullToRefresh'
=== added file 'Dependencies/SSPullToRefresh/LICENSE'
--- Dependencies/SSPullToRefresh/LICENSE 1970-01-01 00:00:00 +0000
+++ Dependencies/SSPullToRefresh/LICENSE 2013-02-11 04:30:25 +0000
@@ -0,0 +1,20 @@
1Copyright (c) 2012 Sam Soffes
2
3Permission is hereby granted, free of charge, to any person obtaining
4a copy of this software and associated documentation files (the
5"Software"), to deal in the Software without restriction, including
6without limitation the rights to use, copy, modify, merge, publish,
7distribute, sublicense, and/or sell copies of the Software, and to
8permit persons to whom the Software is furnished to do so, subject to
9the following conditions:
10
11The above copyright notice and this permission notice shall be
12included in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
021
=== added file 'Dependencies/SSPullToRefresh/SSPullToRefresh.h'
--- Dependencies/SSPullToRefresh/SSPullToRefresh.h 1970-01-01 00:00:00 +0000
+++ Dependencies/SSPullToRefresh/SSPullToRefresh.h 2013-02-11 04:30:25 +0000
@@ -0,0 +1,16 @@
1//
2// SSPullToRefresh.h
3// SSPullToRefresh
4//
5// Created by Sam Soffes on 4/9/12.
6// Copyright (c) 2012 Sam Soffes. All rights reserved.
7//
8
9// Main pull to refresh view. This class contains all of the pulling logic.
10#import "SSPullToRefreshView.h"
11
12// Default content view. Similar to Facebook.
13#import "SSPullToRefreshDefaultContentView.h"
14
15// Simple content view. Similar to Path.
16#import "SSPullToRefreshSimpleContentView.h"
017
=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.h'
--- Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.h 1970-01-01 00:00:00 +0000
+++ Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.h 2013-02-11 04:30:25 +0000
@@ -0,0 +1,17 @@
1//
2// SSPullToRefreshDefaultContentView
3// SSPullToRefresh
4//
5// Created by Sam Soffes on 4/9/12.
6// Copyright (c) 2012 Sam Soffes. All rights reserved.
7//
8
9#import "SSPullToRefreshView.h"
10
11@interface SSPullToRefreshDefaultContentView : UIView <SSPullToRefreshContentView>
12
13@property (nonatomic, strong, readonly) UILabel *statusLabel;
14@property (nonatomic, strong, readonly) UILabel *lastUpdatedAtLabel;
15@property (nonatomic, strong, readonly) UIActivityIndicatorView *activityIndicatorView;
16
17@end
018
=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.m'
--- Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.m 1970-01-01 00:00:00 +0000
+++ Dependencies/SSPullToRefresh/SSPullToRefreshDefaultContentView.m 2013-02-11 04:30:25 +0000
@@ -0,0 +1,86 @@
1//
2// SSPullToRefreshDefaultContentView
3// SSPullToRefresh
4//
5// Created by Sam Soffes on 4/9/12.
6// Copyright (c) 2012 Sam Soffes. All rights reserved.
7//
8
9#import "SSPullToRefreshDefaultContentView.h"
10
11@implementation SSPullToRefreshDefaultContentView
12
13@synthesize statusLabel = _statusLabel;
14@synthesize lastUpdatedAtLabel = _lastUpdatedAtLabel;
15@synthesize activityIndicatorView = _activityIndicatorView;
16
17#pragma mark - UIView
18
19- (id)initWithFrame:(CGRect)frame {
20 if ((self = [super initWithFrame:frame])) {
21 CGFloat width = self.bounds.size.width;
22
23 _statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 14.0f, width, 20.0f)];
24 _statusLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
25 _statusLabel.font = [UIFont boldSystemFontOfSize:14.0f];
26 _statusLabel.textColor = [UIColor blackColor];
27 _statusLabel.backgroundColor = [UIColor clearColor];
28 _statusLabel.textAlignment = UITextAlignmentCenter;
29 [self addSubview:_statusLabel];
30
31 _lastUpdatedAtLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 34.0f, width, 20.0f)];
32 _lastUpdatedAtLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
33 _lastUpdatedAtLabel.font = [UIFont systemFontOfSize:12.0f];
34 _lastUpdatedAtLabel.textColor = [UIColor lightGrayColor];
35 _lastUpdatedAtLabel.backgroundColor = [UIColor clearColor];
36 _lastUpdatedAtLabel.textAlignment = UITextAlignmentCenter;
37 [self addSubview:_lastUpdatedAtLabel];
38
39 _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
40 _activityIndicatorView.frame = CGRectMake(30.0f, 25.0f, 20.0f, 20.0f);
41 [self addSubview:_activityIndicatorView];
42 }
43 return self;
44}
45
46
47#pragma mark - SSPullToRefreshContentView
48
49- (void)setState:(SSPullToRefreshViewState)state withPullToRefreshView:(SSPullToRefreshView *)view {
50 switch (state) {
51 case SSPullToRefreshViewStateReady: {
52 _statusLabel.text = @"Release to refresh...";
53 [_activityIndicatorView stopAnimating];
54 break;
55 }
56
57 case SSPullToRefreshViewStateNormal: {
58 _statusLabel.text = @"Pull down to refresh...";
59 [_activityIndicatorView stopAnimating];
60 break;
61 }
62
63 case SSPullToRefreshViewStateLoading:
64 case SSPullToRefreshViewStateClosing: {
65 _statusLabel.text = @"Loading...";
66 [_activityIndicatorView startAnimating];
67 break;
68 }
69 }
70}
71
72
73- (void)setLastUpdatedAt:(NSDate *)date withPullToRefreshView:(SSPullToRefreshView *)view {
74 static NSDateFormatter *dateFormatter = nil;
75 static dispatch_once_t onceToken;
76 dispatch_once(&onceToken, ^{
77 dateFormatter = [[NSDateFormatter alloc] init];
78 dateFormatter.formatterBehavior = NSDateFormatterBehavior10_4;
79 dateFormatter.dateStyle = NSDateFormatterLongStyle;
80 dateFormatter.timeStyle = NSDateFormatterShortStyle;
81 });
82
83 _lastUpdatedAtLabel.text = [NSString stringWithFormat:@"Last Updated: %@", [dateFormatter stringForObjectValue:date]];
84}
85
86@end
087
=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.h'
--- Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.h 1970-01-01 00:00:00 +0000
+++ Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.h 2013-02-11 04:30:25 +0000
@@ -0,0 +1,16 @@
1//
2// SSPullToRefreshSimpleContentView.h
3// SSPullToRefresh
4//
5// Created by Sam Soffes on 5/17/12.
6// Copyright (c) 2012 Sam Soffes. All rights reserved.
7//
8
9#import "SSPullToRefreshView.h"
10
11@interface SSPullToRefreshSimpleContentView : UIView <SSPullToRefreshContentView>
12
13@property (nonatomic, strong, readonly) UILabel *statusLabel;
14@property (nonatomic, strong, readonly) UIActivityIndicatorView *activityIndicatorView;
15
16@end
017
=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.m'
--- Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.m 1970-01-01 00:00:00 +0000
+++ Dependencies/SSPullToRefresh/SSPullToRefreshSimpleContentView.m 2013-02-11 04:30:25 +0000
@@ -0,0 +1,80 @@
1//
2// SSPullToRefreshSimpleContentView.m
3// SSPullToRefresh
4//
5// Created by Sam Soffes on 5/17/12.
6// Copyright (c) 2012 Sam Soffes. All rights reserved.
7//
8
9#import "SSPullToRefreshSimpleContentView.h"
10
11@implementation SSPullToRefreshSimpleContentView
12
13@synthesize statusLabel = _statusLabel;
14@synthesize activityIndicatorView = _activityIndicatorView;
15
16
17#pragma mark - UIView
18
19- (id)initWithFrame:(CGRect)frame {
20 if ((self = [super initWithFrame:frame])) {
21 CGFloat width = self.bounds.size.width;
22
23 _statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 14.0f, width, 20.0f)];
24 _statusLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
25 _statusLabel.font = [UIFont boldSystemFontOfSize:14.0f];
26 _statusLabel.textColor = [UIColor blackColor];
27 _statusLabel.backgroundColor = [UIColor clearColor];
28 _statusLabel.textAlignment = UITextAlignmentCenter;
29 [self addSubview:_statusLabel];
30
31 _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
32 _activityIndicatorView.frame = CGRectMake(30.0f, 25.0f, 20.0f, 20.0f);
33 [self addSubview:_activityIndicatorView];
34 }
35 return self;
36}
37
38
39- (void)layoutSubviews {
40 CGSize size = self.bounds.size;
41 self.statusLabel.frame = CGRectMake(20.0f, roundf((size.height - 30.0f) / 2.0f), size.width - 40.0f, 30.0f);
42 self.activityIndicatorView.frame = CGRectMake(roundf((size.width - 20.0f) / 2.0f), roundf((size.height - 20.0f) / 2.0f), 20.0f, 20.0f);
43}
44
45
46#pragma mark - SSPullToRefreshContentView
47
48- (void)setState:(SSPullToRefreshViewState)state withPullToRefreshView:(SSPullToRefreshView *)view {
49 switch (state) {
50 case SSPullToRefreshViewStateReady: {
51 self.statusLabel.text = @"Release to refresh";
52 [self.activityIndicatorView startAnimating];
53 self.activityIndicatorView.alpha = 0.0f;
54 break;
55 }
56
57 case SSPullToRefreshViewStateNormal: {
58 self.statusLabel.text = @"Pull down to refresh";
59 self.statusLabel.alpha = 1.0f;
60 [self.activityIndicatorView stopAnimating];
61 self.activityIndicatorView.alpha = 0.0f;
62 break;
63 }
64
65 case SSPullToRefreshViewStateLoading: {
66 self.statusLabel.alpha = 0.0f;
67 [self.activityIndicatorView startAnimating];
68 self.activityIndicatorView.alpha = 1.0f;
69 break;
70 }
71
72 case SSPullToRefreshViewStateClosing: {
73 self.statusLabel.text = nil;
74 self.activityIndicatorView.alpha = 0.0f;
75 break;
76 }
77 }
78}
79
80@end
081
=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshView.h'
--- Dependencies/SSPullToRefresh/SSPullToRefreshView.h 1970-01-01 00:00:00 +0000
+++ Dependencies/SSPullToRefresh/SSPullToRefreshView.h 2013-02-11 04:30:25 +0000
@@ -0,0 +1,205 @@
1//
2// SSPullToRefreshView.h
3// SSPullToRefresh
4//
5// Created by Sam Soffes on 4/9/12.
6// Copyright (c) 2012 Sam Soffes. All rights reserved.
7//
8
9//
10// Example usage:
11//
12// - (void)viewDidLoad {
13// [super viewDidLoad];
14// self.pullToRefreshView = [[SSPullToRefreshView alloc] initWithScrollView:self.tableView delegate:self];
15// }
16//
17// - (void)viewDidUnload {
18// [super viewDidUnload];
19// self.pullToRefreshView = nil;
20// }
21//
22// - (void)refresh {
23// [self.pullToRefreshView startLoading];
24// // Load data...
25// [self.pullToRefreshView finishLoading];
26// }
27//
28// - (void)pullToRefreshViewDidStartLoading:(SSPullToRefreshView *)view {
29// [self refresh];
30// }
31//
32
33typedef enum {
34 /// Most will say "Pull to refresh" in this state
35 SSPullToRefreshViewStateNormal,
36
37 /// Most will say "Release to refresh" in this state
38 SSPullToRefreshViewStateReady,
39
40 /// The view is loading
41 SSPullToRefreshViewStateLoading,
42
43 /// The view has finished loading and is animating
44 SSPullToRefreshViewStateClosing
45} SSPullToRefreshViewState;
46
47@protocol SSPullToRefreshViewDelegate;
48@protocol SSPullToRefreshContentView;
49
50@interface SSPullToRefreshView : UIView
51
52/**
53 The content view displayed when the `scrollView` is pulled down. By default this is an instance of `SSPullToRefreshDefaultContentView`.
54
55 @see SSPullToRefreshContentView
56 */
57@property (nonatomic, strong) UIView<SSPullToRefreshContentView> *contentView;
58
59/**
60 If you need to update the scroll view's content inset while it contains a pull to refresh view, you should set the
61 `defaultContentInset` on the pull to refresh view and it will forward it to the scroll view taking into account the
62 pull to refresh view's position.
63 */
64@property (nonatomic, assign) UIEdgeInsets defaultContentInset;
65
66/**
67 The height of the fully expanded content view. The default is `70.0`.
68
69 The `contentView`'s `sizeThatFits:` will be respected when displayed but does not effect the expanded height. You can use this
70 to draw outside of the expanded area. If you don't implement `sizeThatFits:` it will automatically display at the default size.
71
72 @see expanded
73 */
74@property (nonatomic, assign) CGFloat expandedHeight;
75
76/**
77 A boolean indicating if the pull to refresh view is expanded.
78
79 @see expandedHeight
80 @see startLoadingAndExpand:
81 */
82@property (nonatomic, assign, readonly, getter = isExpanded) BOOL expanded;
83
84/**
85 The scroll view containing the pull to refresh view. This is automatically set with `initWithScrollView:delegate:`.
86
87 @see initWithScrollView:delegate:
88 */
89@property (nonatomic, assign, readonly) UIScrollView *scrollView;
90
91/**
92 The delegate is sent messages when the pull to refresh view starts loading. This is automatically set with `initWithScrollView:delegate:`.
93
94 @see initWithScrollView:delegate:
95 @see SSPullToRefreshViewDelegate
96 */
97@property (nonatomic, weak) id<SSPullToRefreshViewDelegate> delegate;
98
99/**
100 The state of the pull to refresh view.
101
102 @see startLoading
103 @see startLoadingAndExpand:
104 @see finishLoading
105 @see SSPullToRefreshViewState
106 */
107@property (nonatomic, assign, readonly) SSPullToRefreshViewState state;
108
109/**
110 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.
111 You don't have to add it as subview or anything else. The rest is magic.
112
113 You should only initalize with this method and never move it to another scroll view during its lifetime.
114 */
115- (id)initWithScrollView:(UIScrollView *)scrollView delegate:(id<SSPullToRefreshViewDelegate>)delegate;
116
117/**
118 Call this method when you start loading. If you trigger loading another way besides pulling to refresh, call this
119 method so the pull to refresh view will be in sync with the loading status. By default, it will not expand the view
120 so it loads quietly out of view.
121 */
122- (void)startLoading;
123
124/**
125 Call this method when you start loading. If you trigger loading another way besides pulling to refresh, call this
126 method so the pull to refresh view will be in sync with the loading status. You may pass YES for shouldExpand to
127 animate down the pull to refresh view to show that it's loading.
128 */
129- (void)startLoadingAndExpand:(BOOL)shouldExpand;
130/**
131 Call this method if you wish to control animating the expansion.
132 */
133- (void)startLoadingAndExpand:(BOOL)shouldExpand animated:(BOOL)animated;
134
135/**
136 Call this when you finish loading.
137 */
138- (void)finishLoading;
139
140/**
141 Manually update the last updated at time. This will automatically get called when the pull to refresh view finishes laoding.
142 */
143- (void)refreshLastUpdatedAt;
144
145@end
146
147
148@protocol SSPullToRefreshViewDelegate <NSObject>
149
150@optional
151
152/**
153 Return `NO` if the pull to refresh view should no start loading.
154 */
155- (BOOL)pullToRefreshViewShouldStartLoading:(SSPullToRefreshView *)view;
156
157/**
158 The pull to refresh view started loading. You should kick off whatever you need to load when this is called.
159 */
160- (void)pullToRefreshViewDidStartLoading:(SSPullToRefreshView *)view;
161
162/**
163 The pull to refresh view finished loading. This will get called when it receives `finishLoading`.
164 */
165- (void)pullToRefreshViewDidFinishLoading:(SSPullToRefreshView *)view;
166
167/**
168 The date when data was last updated. This will get called when it finishes loading or if it receives `refreshLastUpdatedAt`.
169 Some content views may display this date.
170 */
171- (NSDate *)pullToRefreshViewLastUpdatedAt:(SSPullToRefreshView *)view;
172
173/**
174 The pull to refresh view updated its scroll view's content inset
175 */
176- (void)pullToRefreshView:(SSPullToRefreshView *)view didUpdateContentInset:(UIEdgeInsets)contentInset;
177
178@end
179
180
181@protocol SSPullToRefreshContentView <NSObject>
182
183@required
184
185/**
186 The pull to refresh view's state has changed. The content view must update itself. All content view's must implement
187 this method.
188 */
189- (void)setState:(SSPullToRefreshViewState)state withPullToRefreshView:(SSPullToRefreshView *)view;
190
191@optional
192
193/**
194 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
195 will change to the `SSPullToRefreshViewStateReady` state. You can use this value to draw the progress of the pull
196 (i.e. Tweetbot style).
197 */
198- (void)setPullProgress:(CGFloat)pullProgress;
199
200/**
201 The pull to refresh view updated its last updated date.
202 */
203- (void)setLastUpdatedAt:(NSDate *)date withPullToRefreshView:(SSPullToRefreshView *)view;
204
205@end
0206
=== added file 'Dependencies/SSPullToRefresh/SSPullToRefreshView.m'
--- Dependencies/SSPullToRefresh/SSPullToRefreshView.m 1970-01-01 00:00:00 +0000
+++ Dependencies/SSPullToRefresh/SSPullToRefreshView.m 2013-02-11 04:30:25 +0000
@@ -0,0 +1,349 @@
1//
2// SSPullToRefreshView.m
3// SSPullToRefresh
4//
5// Created by Sam Soffes on 4/9/12.
6// Copyright (c) 2012 Sam Soffes. All rights reserved.
7//
8
9#import "SSPullToRefreshView.h"
10#import "SSPullToRefreshDefaultContentView.h"
11
12@interface SSPullToRefreshView ()
13@property (nonatomic, assign, readwrite) SSPullToRefreshViewState state;
14@property (nonatomic, assign, readwrite) UIScrollView *scrollView;
15@property (nonatomic, assign, readwrite, getter = isExpanded) BOOL expanded;
16- (void)_setContentInsetTop:(CGFloat)topInset;
17- (void)_setState:(SSPullToRefreshViewState)state animated:(BOOL)animated expanded:(BOOL)expanded completion:(void (^)(void))completion;
18- (void)_setPullProgress:(CGFloat)pullProgress;
19@end
20
21@implementation SSPullToRefreshView {
22 dispatch_semaphore_t _animationSemaphore;
23 CGFloat _topInset;
24}
25
26@synthesize delegate = _delegate;
27@synthesize scrollView = _scrollView;
28@synthesize expandedHeight = _expandedHeight;
29@synthesize contentView = _contentView;
30@synthesize state = _state;
31@synthesize expanded = _expanded;
32@synthesize defaultContentInset = _defaultContentInset;
33
34
35#pragma mark - Accessors
36
37- (void)setState:(SSPullToRefreshViewState)state {
38 BOOL wasLoading = _state == SSPullToRefreshViewStateLoading;
39 _state = state;
40
41 // Forward to content view
42 [self.contentView setState:_state withPullToRefreshView:self];
43
44 // Update delegate
45 if (wasLoading && _state != SSPullToRefreshViewStateLoading) {
46 if ([_delegate respondsToSelector:@selector(pullToRefreshViewDidFinishLoading:)]) {
47 [_delegate pullToRefreshViewDidFinishLoading:self];
48 }
49 } else if (!wasLoading && _state == SSPullToRefreshViewStateLoading) {
50 [self _setPullProgress:1.0f];
51 if ([_delegate respondsToSelector:@selector(pullToRefreshViewDidStartLoading:)]) {
52 [_delegate pullToRefreshViewDidStartLoading:self];
53 }
54 }
55}
56
57
58- (void)setExpanded:(BOOL)expanded {
59 _expanded = expanded;
60 [self _setContentInsetTop:expanded ? self.expandedHeight : 0.0f];
61}
62
63
64- (void)setScrollView:(UIScrollView *)scrollView {
65 void *context = (__bridge void *)self;
66 if ([_scrollView respondsToSelector:@selector(removeObserver:forKeyPath:context:)]) {
67 [_scrollView removeObserver:self forKeyPath:@"contentOffset" context:context];
68 } else if (_scrollView) {
69 [_scrollView removeObserver:self forKeyPath:@"contentOffset"];
70 }
71
72 _scrollView = scrollView;
73 _defaultContentInset = _scrollView.contentInset;
74 [_scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:context];
75}
76
77
78- (UIView<SSPullToRefreshContentView> *)contentView {
79 // Use the simple content view as the default
80 if (!_contentView) {
81 self.contentView = [[SSPullToRefreshDefaultContentView alloc] initWithFrame:CGRectZero];
82 }
83 return _contentView;
84}
85
86
87- (void)setContentView:(UIView<SSPullToRefreshContentView> *)contentView {
88 [_contentView removeFromSuperview];
89 _contentView = contentView;
90
91 _contentView.autoresizingMask = UIViewAutoresizingNone;
92 [_contentView setState:_state withPullToRefreshView:self];
93 [self refreshLastUpdatedAt];
94 [self addSubview:_contentView];
95}
96
97
98- (void)setDefaultContentInset:(UIEdgeInsets)defaultContentInset {
99 _defaultContentInset = defaultContentInset;
100 [self _setContentInsetTop:_topInset];
101}
102
103
104#pragma mark - NSObject
105
106- (void)dealloc {
107 self.scrollView = nil;
108 self.delegate = nil;
109#if !OS_OBJECT_USE_OBJC
110 dispatch_release(_animationSemaphore);
111#endif
112}
113
114
115#pragma mark - UIView
116
117- (void)removeFromSuperview {
118 self.scrollView = nil;
119 [super removeFromSuperview];
120}
121
122
123- (void)layoutSubviews {
124 CGSize size = self.bounds.size;
125 CGSize contentSize = [self.contentView sizeThatFits:size];
126
127 if (contentSize.width < size.width) {
128 contentSize.width = size.width;
129 }
130
131 if (contentSize.height < _expandedHeight) {
132 contentSize.height = _expandedHeight;
133 }
134
135 self.contentView.frame = CGRectMake(roundf((size.width - contentSize.width) / 2.0f), size.height - contentSize.height, contentSize.width, contentSize.height);
136}
137
138
139#pragma mark - Initializer
140
141- (id)initWithScrollView:(UIScrollView *)scrollView delegate:(id<SSPullToRefreshViewDelegate>)delegate {
142 CGRect frame = CGRectMake(0.0f, 0.0f - scrollView.bounds.size.height, scrollView.bounds.size.width,
143 scrollView.bounds.size.height);
144 if ((self = [self initWithFrame:frame])) {
145 self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
146 self.scrollView = scrollView;
147 self.delegate = delegate;
148 self.state = SSPullToRefreshViewStateNormal;
149 self.expandedHeight = 70.0f;
150
151 for (UIView *view in self.scrollView.subviews) {
152 if ([view isKindOfClass:[SSPullToRefreshView class]]) {
153 [[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];
154 }
155 }
156
157 // Add to scroll view
158 [self.scrollView addSubview:self];
159
160 // Semaphore is used to ensure only one animation plays at a time
161 _animationSemaphore = dispatch_semaphore_create(0);
162 dispatch_semaphore_signal(_animationSemaphore);
163 }
164 return self;
165}
166
167
168#pragma mark - Loading
169
170- (void)startLoading {
171 [self startLoadingAndExpand:NO animated:NO];
172}
173
174
175- (void)startLoadingAndExpand:(BOOL)shouldExpand {
176 [self startLoadingAndExpand:shouldExpand animated:YES];
177}
178
179
180- (void)startLoadingAndExpand:(BOOL)shouldExpand animated:(BOOL)animated {
181 // If we're not loading, this method has no effect
182 if (_state == SSPullToRefreshViewStateLoading) {
183 return;
184 }
185
186 // Animate back to the loading state
187 [self _setState:SSPullToRefreshViewStateLoading animated:animated expanded:shouldExpand completion:nil];
188}
189
190
191- (void)finishLoading {
192 // If we're not loading, this method has no effect
193 if (_state != SSPullToRefreshViewStateLoading) {
194 return;
195 }
196
197 // Animate back to the normal state
198 __weak SSPullToRefreshView *blockSelf = self;
199 [self _setState:SSPullToRefreshViewStateClosing animated:YES expanded:NO completion:^{
200 blockSelf.state = SSPullToRefreshViewStateNormal;
201 }];
202}
203
204
205- (void)refreshLastUpdatedAt {
206 NSDate *date = nil;
207 if ([_delegate respondsToSelector:@selector(pullToRefreshViewLastUpdatedAt:)]) {
208 date = [_delegate pullToRefreshViewLastUpdatedAt:self];
209 } else {
210 date = [NSDate date];
211 }
212
213 // Forward to content view
214 if ([self.contentView respondsToSelector:@selector(setLastUpdatedAt:withPullToRefreshView:)]) {
215 [self.contentView setLastUpdatedAt:date withPullToRefreshView:self];
216 }
217}
218
219
220#pragma mark - Private
221
222- (void)_setContentInsetTop:(CGFloat)topInset {
223 _topInset = topInset;
224
225 // Default to the scroll view's initial content inset
226 UIEdgeInsets inset = _defaultContentInset;
227
228 // Add the top inset
229 inset.top += _topInset;
230
231 // Don't set it if that is already the current inset
232 if (UIEdgeInsetsEqualToEdgeInsets(_scrollView.contentInset, inset)) {
233 return;
234 }
235
236 // Update the content inset
237 _scrollView.contentInset = inset;
238
239 // If scrollView is on top, scroll again to the top (needed for scrollViews with content > scrollView).
240 if (_scrollView.contentOffset.y == 0) {
241 [_scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
242 }
243
244 // Tell the delegate
245 if ([self.delegate respondsToSelector:@selector(pullToRefreshView:didUpdateContentInset:)]) {
246 [self.delegate pullToRefreshView:self didUpdateContentInset:_scrollView.contentInset];
247 }
248}
249
250
251- (void)_setState:(SSPullToRefreshViewState)state animated:(BOOL)animated expanded:(BOOL)expanded completion:(void (^)(void))completion {
252 if (!animated) {
253 self.state = state;
254 self.expanded = expanded;
255
256 if (completion) {
257 completion();
258 }
259 return;
260 }
261
262 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
263 dispatch_semaphore_wait(_animationSemaphore, DISPATCH_TIME_FOREVER);
264 dispatch_async(dispatch_get_main_queue(), ^{
265 [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
266 self.state = state;
267 self.expanded = expanded;
268 } completion:^(BOOL finished) {
269 dispatch_semaphore_signal(_animationSemaphore);
270 if (completion) {
271 completion();
272 }
273 }];
274 });
275 });
276}
277
278
279- (void)_setPullProgress:(CGFloat)pullProgress {
280 if ([self.contentView respondsToSelector:@selector(setPullProgress:)]) {
281 [self.contentView setPullProgress:pullProgress];
282 }
283}
284
285
286#pragma mark - NSKeyValueObserving
287
288- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
289 // Call super if we didn't register for this notification
290 if (context != (__bridge void *)self) {
291 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
292 return;
293 }
294
295 // We don't care about this notificaiton
296 if (object != _scrollView || ![keyPath isEqualToString:@"contentOffset"]) {
297 return;
298 }
299
300 // Get the offset out of the change notification
301 CGFloat y = [[change objectForKey:NSKeyValueChangeNewKey] CGPointValue].y + _defaultContentInset.top;
302
303 // Scroll view is dragging
304 if (_scrollView.isDragging) {
305 // Scroll view is ready
306 if (_state == SSPullToRefreshViewStateReady) {
307 // Dragged enough to refresh
308 if (y > -_expandedHeight && y < 0.0f) {
309 self.state = SSPullToRefreshViewStateNormal;
310 }
311 // Scroll view is normal
312 } else if (_state == SSPullToRefreshViewStateNormal) {
313 // Update the content view's pulling progressing
314 [self _setPullProgress:-y / _expandedHeight];
315
316 // Dragged enough to be ready
317 if (y < -_expandedHeight) {
318 self.state = SSPullToRefreshViewStateReady;
319 }
320 // Scroll view is loading
321 } else if (_state == SSPullToRefreshViewStateLoading) {
322 [self _setContentInsetTop:_expandedHeight];
323 }
324 return;
325 }
326
327 // If the scroll view isn't ready, we're not interested
328 if (_state != SSPullToRefreshViewStateReady) {
329 return;
330 }
331
332 // We're ready, prepare to switch to loading. Be default, we should refresh.
333 SSPullToRefreshViewState newState = SSPullToRefreshViewStateLoading;
334
335 // Ask the delegate if it's cool to start loading
336 BOOL expand = YES;
337 if ([_delegate respondsToSelector:@selector(pullToRefreshViewShouldStartLoading:)]) {
338 if (![_delegate pullToRefreshViewShouldStartLoading:self]) {
339 // Animate back to normal since the delegate said no
340 newState = SSPullToRefreshViewStateNormal;
341 expand = NO;
342 }
343 }
344
345 // Animate to the new state
346 [self _setState:newState animated:YES expanded:expand completion:nil];
347}
348
349@end
0350
=== modified file 'Music/Models/Album.h'
--- Music/Models/Album.h 2013-01-24 19:08:51 +0000
+++ Music/Models/Album.h 2013-02-11 04:30:25 +0000
@@ -27,4 +27,6 @@
2727
28@property (nonatomic, retain) NSSet *songs;28@property (nonatomic, retain) NSSet *songs;
2929
30- (NSSet *)songsForArtistId:(NSString *)artistId;
31
30@end32@end
3133
=== modified file 'Music/Models/Album.m'
--- Music/Models/Album.m 2013-01-24 19:08:51 +0000
+++ Music/Models/Album.m 2013-02-11 04:30:25 +0000
@@ -28,6 +28,11 @@
2828
29@dynamic songs;29@dynamic songs;
3030
31- (NSSet *)songsForArtistId:(NSString *)_artistId {
32 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"artist.artistId == %@ OR albumArtist.artistId == %@", _artistId, _artistId];
33 return [self.songs filteredSetUsingPredicate:predicate];
34}
35
31#pragma mark - UOModel methods36#pragma mark - UOModel methods
3237
33+ (RKEntityMapping *)objectMapping {38+ (RKEntityMapping *)objectMapping {
3439
=== modified file 'Music/Storyboard_iPhone.storyboard'
--- Music/Storyboard_iPhone.storyboard 2013-02-11 04:30:25 +0000
+++ Music/Storyboard_iPhone.storyboard 2013-02-11 04:30:25 +0000
@@ -44,17 +44,17 @@
44 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>44 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
45 <autoresizingMask key="autoresizingMask"/>45 <autoresizingMask key="autoresizingMask"/>
46 <subviews>46 <subviews>
47 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Wy4-11-qhh">47 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Wy4-11-qhh">
48 <rect key="frame" x="53" y="2" width="38" height="22"/>48 <rect key="frame" x="53" y="1" width="45" height="23"/>
49 <autoresizingMask key="autoresizingMask"/>49 <autoresizingMask key="autoresizingMask"/>
50 <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>50 <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
51 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>51 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
52 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>52 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
53 </label>53 </label>
54 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="PUO-bG-eLt">54 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="2 albums" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="PUO-bG-eLt">
55 <rect key="frame" x="53" y="24" width="47" height="18"/>55 <rect key="frame" x="53" y="24" width="56" height="18"/>
56 <autoresizingMask key="autoresizingMask"/>56 <autoresizingMask key="autoresizingMask"/>
57 <fontDescription key="fontDescription" type="system" pointSize="14"/>57 <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
58 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>58 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>
59 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>59 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
60 </label>60 </label>
@@ -86,7 +86,7 @@
86 </objects>86 </objects>
87 <point key="canvasLocation" x="1047" y="-56"/>87 <point key="canvasLocation" x="1047" y="-56"/>
88 </scene>88 </scene>
89 <!--Artist View Controller-->89 <!--Artist View Controller - Artist-->
90 <scene sceneID="Y3A-G3-aF2">90 <scene sceneID="Y3A-G3-aF2">
91 <objects>91 <objects>
92 <viewController storyboardIdentifier="ArtistViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="EDS-Dj-fjS" customClass="ArtistViewController" sceneMemberID="viewController">92 <viewController storyboardIdentifier="ArtistViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="EDS-Dj-fjS" customClass="ArtistViewController" sceneMemberID="viewController">
@@ -106,17 +106,17 @@
106 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>106 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
107 <autoresizingMask key="autoresizingMask"/>107 <autoresizingMask key="autoresizingMask"/>
108 <subviews>108 <subviews>
109 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="G72-IX-jh9">109 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Album" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="G72-IX-jh9">
110 <rect key="frame" x="53" y="2" width="38" height="22"/>110 <rect key="frame" x="53" y="1" width="54" height="23"/>
111 <autoresizingMask key="autoresizingMask"/>111 <autoresizingMask key="autoresizingMask"/>
112 <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>112 <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
113 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>113 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
114 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>114 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
115 </label>115 </label>
116 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VIc-Rn-YXm">116 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="11 songs" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VIc-Rn-YXm">
117 <rect key="frame" x="53" y="24" width="47" height="18"/>117 <rect key="frame" x="53" y="24" width="57" height="18"/>
118 <autoresizingMask key="autoresizingMask"/>118 <autoresizingMask key="autoresizingMask"/>
119 <fontDescription key="fontDescription" type="system" pointSize="14"/>119 <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
120 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>120 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>
121 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>121 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
122 </label>122 </label>
@@ -133,29 +133,29 @@
133 </tableViewCell>133 </tableViewCell>
134 </prototypes>134 </prototypes>
135 </tableView>135 </tableView>
136 <imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="7CD-jP-Egt">136 <imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="default-album-art-200.png" id="7CD-jP-Egt">
137 <rect key="frame" x="0.0" y="0.0" width="120" height="120"/>137 <rect key="frame" x="5" y="5" width="110" height="110"/>
138 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>138 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
139 </imageView>139 </imageView>
140 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="00M-r3-Ezp">140 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="00M-r3-Ezp">
141 <rect key="frame" x="128" y="15" width="192" height="26"/>141 <rect key="frame" x="123" y="5" width="172" height="26"/>
142 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>142 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
143 <fontDescription key="fontDescription" type="system" pointSize="18"/>143 <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
144 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>144 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
145 <nil key="highlightedColor"/>145 <nil key="highlightedColor"/>
146 <color key="shadowColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>146 <color key="shadowColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
147 </label>147 </label>
148 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="6 Albums" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="25K-Tf-YSQ">148 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="2 Albums" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="25K-Tf-YSQ">
149 <rect key="frame" x="128" y="49" width="63" height="21"/>149 <rect key="frame" x="123" y="39" width="172" height="21"/>
150 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>150 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
151 <fontDescription key="fontDescription" type="system" pointSize="15"/>151 <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="15"/>
152 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>152 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
153 <color key="highlightedColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>153 <color key="highlightedColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
154 </label>154 </label>
155 </subviews>155 </subviews>
156 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>156 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
157 </view>157 </view>
158 <navigationItem key="navigationItem" id="OM6-Kh-nGV">158 <navigationItem key="navigationItem" title="Artist" id="OM6-Kh-nGV">
159 <barButtonItem key="rightBarButtonItem" title="Now Playing" id="tdG-O0-4fc">159 <barButtonItem key="rightBarButtonItem" title="Now Playing" id="tdG-O0-4fc">
160 <connections>160 <connections>
161 <segue destination="hRX-A4-gMJ" kind="modal" id="dlu-4W-myG"/>161 <segue destination="hRX-A4-gMJ" kind="modal" id="dlu-4W-myG"/>
@@ -174,7 +174,7 @@
174 </objects>174 </objects>
175 <point key="canvasLocation" x="1515" y="-56"/>175 <point key="canvasLocation" x="1515" y="-56"/>
176 </scene>176 </scene>
177 <!--Album View Controller-->177 <!--Album View Controller - Album-->
178 <scene sceneID="24B-tK-YA0">178 <scene sceneID="24B-tK-YA0">
179 <objects>179 <objects>
180 <viewController storyboardIdentifier="AlbumViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="8Ya-S8-w02" customClass="AlbumViewController" sceneMemberID="viewController">180 <viewController storyboardIdentifier="AlbumViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="8Ya-S8-w02" customClass="AlbumViewController" sceneMemberID="viewController">
@@ -214,14 +214,14 @@
214 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="60:00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6u8-pY-Fu0">214 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="60:00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6u8-pY-Fu0">
215 <rect key="frame" x="242" y="10" width="46" height="21"/>215 <rect key="frame" x="242" y="10" width="46" height="21"/>
216 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>216 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
217 <fontDescription key="fontDescription" type="system" pointSize="12"/>217 <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="12"/>
218 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>218 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
219 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>219 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
220 </label>220 </label>
221 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QJk-JO-Hhy">221 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QJk-JO-Hhy">
222 <rect key="frame" x="20" y="10" width="198" height="21"/>222 <rect key="frame" x="20" y="8" width="198" height="27"/>
223 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>223 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
224 <fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>224 <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
225 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>225 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
226 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>226 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
227 </label>227 </label>
@@ -250,29 +250,29 @@
250 </tableViewCell>250 </tableViewCell>
251 </prototypes>251 </prototypes>
252 </tableView>252 </tableView>
253 <imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="1ZL-7v-l7S">253 <imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="default-album-art-200.png" id="1ZL-7v-l7S">
254 <rect key="frame" x="0.0" y="0.0" width="120" height="120"/>254 <rect key="frame" x="5" y="5" width="110" height="110"/>
255 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>255 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
256 </imageView>256 </imageView>
257 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Album" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dwO-gw-nTE">257 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Album" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dwO-gw-nTE">
258 <rect key="frame" x="128" y="15" width="192" height="26"/>258 <rect key="frame" x="123" y="5" width="192" height="26"/>
259 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>259 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
260 <fontDescription key="fontDescription" type="system" pointSize="18"/>260 <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
261 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>261 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
262 <nil key="highlightedColor"/>262 <nil key="highlightedColor"/>
263 <color key="shadowColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>263 <color key="shadowColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
264 </label>264 </label>
265 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="G01-4v-qdB">265 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="G01-4v-qdB">
266 <rect key="frame" x="128" y="49" width="63" height="21"/>266 <rect key="frame" x="123" y="39" width="172" height="21"/>
267 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>267 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
268 <fontDescription key="fontDescription" type="system" pointSize="15"/>268 <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="15"/>
269 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>269 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
270 <color key="highlightedColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>270 <color key="highlightedColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
271 </label>271 </label>
272 </subviews>272 </subviews>
273 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>273 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
274 </view>274 </view>
275 <navigationItem key="navigationItem" id="a61-1v-dxt">275 <navigationItem key="navigationItem" title="Album" id="a61-1v-dxt">
276 <barButtonItem key="rightBarButtonItem" title="Now Playing" id="wGH-uZ-TGk">276 <barButtonItem key="rightBarButtonItem" title="Now Playing" id="wGH-uZ-TGk">
277 <connections>277 <connections>
278 <segue destination="hRX-A4-gMJ" kind="modal" identifier="NowPlayingSegue" id="YJd-qV-EXm"/>278 <segue destination="hRX-A4-gMJ" kind="modal" identifier="NowPlayingSegue" id="YJd-qV-EXm"/>
@@ -395,7 +395,7 @@
395 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="0:00" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" id="pNU-Jr-yiV">395 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="0:00" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" id="pNU-Jr-yiV">
396 <rect key="frame" x="12" y="31" width="42" height="15"/>396 <rect key="frame" x="12" y="31" width="42" height="15"/>
397 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>397 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
398 <fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="14"/>398 <fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
399 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>399 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
400 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>400 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
401 <color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>401 <color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
@@ -403,7 +403,7 @@
403 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="10:00" lineBreakMode="tailTruncation" minimumFontSize="10" id="fsV-6P-1Kb">403 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="10:00" lineBreakMode="tailTruncation" minimumFontSize="10" id="fsV-6P-1Kb">
404 <rect key="frame" x="266" y="31" width="42" height="15"/>404 <rect key="frame" x="266" y="31" width="42" height="15"/>
405 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>405 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
406 <fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="14"/>406 <fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
407 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>407 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
408 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>408 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
409 <color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>409 <color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
@@ -411,7 +411,7 @@
411 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="3 of 100" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" id="KsY-jo-7df">411 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="3 of 100" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" id="KsY-jo-7df">
412 <rect key="frame" x="12" y="5" width="296" height="19"/>412 <rect key="frame" x="12" y="5" width="296" height="19"/>
413 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>413 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
414 <fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="14"/>414 <fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="14"/>
415 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>415 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
416 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>416 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
417 <color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>417 <color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
@@ -528,17 +528,17 @@
528 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>528 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
529 <autoresizingMask key="autoresizingMask"/>529 <autoresizingMask key="autoresizingMask"/>
530 <subviews>530 <subviews>
531 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="lHq-bS-Zm9">531 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Album" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="lHq-bS-Zm9">
532 <rect key="frame" x="53" y="2" width="38" height="22"/>532 <rect key="frame" x="53" y="1" width="54" height="23"/>
533 <autoresizingMask key="autoresizingMask"/>533 <autoresizingMask key="autoresizingMask"/>
534 <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>534 <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
535 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>535 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
536 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>536 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
537 </label>537 </label>
538 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="03B-YT-E7s">538 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Artist" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="03B-YT-E7s">
539 <rect key="frame" x="53" y="24" width="47" height="18"/>539 <rect key="frame" x="53" y="24" width="31" height="18"/>
540 <autoresizingMask key="autoresizingMask"/>540 <autoresizingMask key="autoresizingMask"/>
541 <fontDescription key="fontDescription" type="system" pointSize="14"/>541 <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
542 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>542 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>
543 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>543 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
544 </label>544 </label>
@@ -579,41 +579,35 @@
579 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>579 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
580 <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>580 <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
581 <prototypes>581 <prototypes>
582 <tableViewCell contentMode="scaleToFill" selectionStyle="gray" indentationWidth="10" reuseIdentifier="Cell" id="EzM-5L-53C" customClass="SongCell">582 <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">
583 <rect key="frame" x="0.0" y="22" width="320" height="44"/>583 <rect key="frame" x="0.0" y="22" width="320" height="44"/>
584 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>584 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
585 <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">585 <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
586 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>586 <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
587 <autoresizingMask key="autoresizingMask"/>587 <autoresizingMask key="autoresizingMask"/>
588 <subviews>588 <subviews>
589 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="URM-e4-2yc">589 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="FGV-Wb-IfK">
590 <rect key="frame" x="20" y="11" width="31" height="21"/>590 <rect key="frame" x="53" y="1" width="36" height="23"/>
591 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>591 <autoresizingMask key="autoresizingMask"/>
592 <fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>592 <fontDescription key="fontDescription" name="HelveticaNeue-Medium" family="Helvetica Neue" pointSize="18"/>
593 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>593 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
594 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>594 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
595 </label>595 </label>
596 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="60:00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="vws-t4-lyH">596 <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Subtitle" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="i6c-1Q-zdc">
597 <rect key="frame" x="254" y="11" width="46" height="21"/>597 <rect key="frame" x="53" y="24" width="46" height="18"/>
598 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>598 <autoresizingMask key="autoresizingMask"/>
599 <fontDescription key="fontDescription" type="system" pointSize="12"/>599 <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
600 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>600 <color key="textColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="calibratedRGB"/>
601 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>601 <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
602 </label>602 </label>
603 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="cHc-6o-Le1">603 <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="default-album-art-120.png" id="eTE-GK-6eP">
604 <rect key="frame" x="59" y="11" width="198" height="21"/>604 <rect key="frame" x="0.0" y="0.0" width="43" height="43"/>
605 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>605 <autoresizingMask key="autoresizingMask"/>
606 <fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>606 </imageView>
607 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
608 <color key="highlightedColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
609 </label>
610 </subviews>607 </subviews>
611 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>608 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
612 </view>609 </view>
613 <connections>610 <connections>
614 <outlet property="clock" destination="vws-t4-lyH" id="Fb3-aG-7Ym"/>
615 <outlet property="title" destination="cHc-6o-Le1" id="XJD-HL-AWU"/>
616 <outlet property="track" destination="URM-e4-2yc" id="CNs-EV-tVd"/>
617 <segue destination="hRX-A4-gMJ" kind="modal" id="ebj-wD-yft"/>611 <segue destination="hRX-A4-gMJ" kind="modal" id="ebj-wD-yft"/>
618 </connections>612 </connections>
619 </tableViewCell>613 </tableViewCell>
@@ -960,24 +954,22 @@
960 </scene>954 </scene>
961 </scenes>955 </scenes>
962 <resources>956 <resources>
963 <image name="03-loopback.png" width="32" height="22"/>957 <image name="03-loopback.png" width="16" height="16"/>
964 <image name="05-shuffle.png" width="28" height="20"/>958 <image name="05-shuffle.png" width="16" height="16"/>
965 <image name="albums.png" width="30" height="30"/>959 <image name="albums.png" width="16" height="16"/>
966 <image name="artists.png" width="30" height="30"/>960 <image name="artists.png" width="16" height="16"/>
967 <image name="default-album-art-120.png" width="60" height="60"/>961 <image name="default-album-art-120.png" width="16" height="16"/>
968 <image name="default-album-art-640.png" width="320" height="320"/>962 <image name="default-album-art-200.png" width="16" height="16"/>
969 <image name="download-grey.png" width="28" height="28"/>963 <image name="default-album-art-640.png" width="16" height="16"/>
970 <image name="download.png" width="28" height="28"/>964 <image name="download-grey.png" width="16" height="16"/>
971 <image name="grabber.png" width="22" height="44"/>965 <image name="download.png" width="16" height="16"/>
972 <image name="player_overlay_bg.png" width="320" height="100"/>966 <image name="grabber.png" width="16" height="16"/>
973 <image name="playlists.png" width="30" height="30"/>967 <image name="player_overlay_bg.png" width="16" height="16"/>
974 <image name="settings.png" width="30" height="30"/>968 <image name="playlists.png" width="16" height="16"/>
975 <image name="songs.png" width="30" height="30"/>969 <image name="settings.png" width="16" height="16"/>
970 <image name="songs.png" width="16" height="16"/>
976 </resources>971 </resources>
977 <classes>972 <classes>
978 <class className="AlbumCell" superclassName="UITableViewCell">
979 <source key="sourceIdentifier" type="project" relativePath="./Classes/AlbumCell.h"/>
980 </class>
981 <class className="AlbumViewController" superclassName="UIViewController">973 <class className="AlbumViewController" superclassName="UIViewController">
982 <source key="sourceIdentifier" type="project" relativePath="./Classes/AlbumViewController.h"/>974 <source key="sourceIdentifier" type="project" relativePath="./Classes/AlbumViewController.h"/>
983 <relationships>975 <relationships>
@@ -988,69 +980,16 @@
988 <relationship kind="outlet" name="tableView" candidateClass="UITableView"/>980 <relationship kind="outlet" name="tableView" candidateClass="UITableView"/>
989 </relationships>981 </relationships>
990 </class>982 </class>
991 <class className="AlbumsViewController" superclassName="UOIndexedViewController">983 <class className="ArtistViewController">
992 <source key="sourceIdentifier" type="project" relativePath="./Classes/AlbumsViewController.h"/>
993 </class>
994 <class className="ArtistCell" superclassName="UITableViewCell">
995 <source key="sourceIdentifier" type="project" relativePath="./Classes/ArtistCell.h"/>
996 </class>
997 <class className="ArtistViewController" superclassName="UIViewController">
998 <source key="sourceIdentifier" type="project" relativePath="./Classes/ArtistViewController.h"/>984 <source key="sourceIdentifier" type="project" relativePath="./Classes/ArtistViewController.h"/>
999 <relationships>985 <relationships>
1000 <relationship kind="outlet" name="albumArt" candidateClass="UIImageView"/>986 <relationship kind="outlet" name="albumArt" candidateClass="UIImageView"/>
1001 <relationship kind="outlet" name="artistDescription" candidateClass="UILabel"/>987 <relationship kind="outlet" name="artistDescription" candidateClass="UILabel"/>
1002 <relationship kind="outlet" name="artistName" candidateClass="UILabel"/>988 <relationship kind="outlet" name="artistName" candidateClass="UILabel"/>
1003 <relationship kind="outlet" name="nowPlayingButton" candidateClass="UIBarButtonItem"/>
1004 <relationship kind="outlet" name="tableView" candidateClass="UITableView"/>989 <relationship kind="outlet" name="tableView" candidateClass="UITableView"/>
1005 </relationships>990 </relationships>
1006 </class>991 </class>
1007 <class className="ArtistsViewController" superclassName="UOIndexedViewController">992 <class className="SettingsAuthenticationViewController">
1008 <source key="sourceIdentifier" type="project" relativePath="./Classes/ArtistsViewController.h"/>
1009 </class>
1010 <class className="PlayerHeaderView" superclassName="UIView">
1011 <source key="sourceIdentifier" type="project" relativePath="./Classes/PlayerHeaderView.h"/>
1012 <relationships>
1013 <relationship kind="outlet" name="album" candidateClass="UILabel"/>
1014 <relationship kind="outlet" name="artist" candidateClass="UILabel"/>
1015 <relationship kind="outlet" name="title" candidateClass="UILabel"/>
1016 </relationships>
1017 </class>
1018 <class className="PlayerViewController" superclassName="UIViewController">
1019 <source key="sourceIdentifier" type="project" relativePath="./Classes/PlayerViewController.h"/>
1020 <relationships>
1021 <relationship kind="action" name="changeRepeat:"/>
1022 <relationship kind="action" name="changeShuffle:"/>
1023 <relationship kind="action" name="hide:"/>
1024 <relationship kind="action" name="next:"/>
1025 <relationship kind="action" name="pause:"/>
1026 <relationship kind="action" name="play:"/>
1027 <relationship kind="action" name="previous:"/>
1028 <relationship kind="action" name="toggleExtendedControls:"/>
1029 <relationship kind="outlet" name="albumart" candidateClass="UIButton"/>
1030 <relationship kind="outlet" name="artistLabel" candidateClass="UILabel"/>
1031 <relationship kind="outlet" name="controlBar" candidateClass="UIToolbar"/>
1032 <relationship kind="outlet" name="elapsedTime" candidateClass="UILabel"/>
1033 <relationship kind="outlet" name="extendedControlView" candidateClass="UIView"/>
1034 <relationship kind="outlet" name="nextButton" candidateClass="UIBarButtonItem"/>
1035 <relationship kind="outlet" name="pauseButton" candidateClass="UIBarButtonItem"/>
1036 <relationship kind="outlet" name="playButton" candidateClass="UIBarButtonItem"/>
1037 <relationship kind="outlet" name="playlistInfo" candidateClass="UILabel"/>
1038 <relationship kind="outlet" name="previousButton" candidateClass="UIBarButtonItem"/>
1039 <relationship kind="outlet" name="progressView" candidateClass="UISlider"/>
1040 <relationship kind="outlet" name="reflectionImage" candidateClass="UIImageView"/>
1041 <relationship kind="outlet" name="repeatButton" candidateClass="UIButton"/>
1042 <relationship kind="outlet" name="shuffleButton" candidateClass="UIButton"/>
1043 <relationship kind="outlet" name="titleLabel" candidateClass="UILabel"/>
1044 <relationship kind="outlet" name="totalTime" candidateClass="UILabel"/>
1045 </relationships>
1046 </class>
1047 <class className="PlaylistCell" superclassName="UITableViewCell">
1048 <source key="sourceIdentifier" type="project" relativePath="./Classes/PlaylistCell.h"/>
1049 </class>
1050 <class className="PlaylistsViewController" superclassName="UOIndexedViewController">
1051 <source key="sourceIdentifier" type="project" relativePath="./Classes/PlaylistsViewController.h"/>
1052 </class>
1053 <class className="SettingsAuthenticationViewController" superclassName="UIViewController">
1054 <source key="sourceIdentifier" type="project" relativePath="./Classes/SettingsAuthenticationViewController.h"/>993 <source key="sourceIdentifier" type="project" relativePath="./Classes/SettingsAuthenticationViewController.h"/>
1055 <relationships>994 <relationships>
1056 <relationship kind="action" name="authenticate:"/>995 <relationship kind="action" name="authenticate:"/>
@@ -1060,7 +999,7 @@
1060 <relationship kind="outlet" name="usernameField" candidateClass="UITextField"/>999 <relationship kind="outlet" name="usernameField" candidateClass="UITextField"/>
1061 </relationships>1000 </relationships>
1062 </class>1001 </class>
1063 <class className="SettingsViewController" superclassName="UITableViewController">1002 <class className="SettingsViewController">
1064 <source key="sourceIdentifier" type="project" relativePath="./Classes/SettingsViewController.h"/>1003 <source key="sourceIdentifier" type="project" relativePath="./Classes/SettingsViewController.h"/>
1065 <relationships>1004 <relationships>
1066 <relationship kind="action" name="deleteCaches:"/>1005 <relationship kind="action" name="deleteCaches:"/>
@@ -1071,27 +1010,6 @@
1071 <relationship kind="outlet" name="versionNumber" candidateClass="UILabel"/>1010 <relationship kind="outlet" name="versionNumber" candidateClass="UILabel"/>
1072 </relationships>1011 </relationships>
1073 </class>1012 </class>
1074 <class className="SongCell" superclassName="UITableViewCell">
1075 <source key="sourceIdentifier" type="project" relativePath="./Classes/SongCell.h"/>
1076 <relationships>
1077 <relationship kind="outlet" name="cacheImageIndicator" candidateClass="UIImageView"/>
1078 <relationship kind="outlet" name="cacheIndicator" candidateClass="UIView"/>
1079 <relationship kind="outlet" name="clock" candidateClass="UILabel"/>
1080 <relationship kind="outlet" name="handle" candidateClass="UIImageView"/>
1081 <relationship kind="outlet" name="title" candidateClass="UILabel"/>
1082 <relationship kind="outlet" name="topView" candidateClass="UIView"/>
1083 <relationship kind="outlet" name="track" candidateClass="UILabel"/>
1084 </relationships>
1085 </class>
1086 <class className="SongsViewController" superclassName="UOIndexedViewController">
1087 <source key="sourceIdentifier" type="project" relativePath="./Classes/SongsViewController.h"/>
1088 </class>
1089 <class className="UOIndexedViewController" superclassName="UITableViewController">
1090 <source key="sourceIdentifier" type="project" relativePath="./Classes/UOIndexedViewController.h"/>
1091 <relationships>
1092 <relationship kind="outlet" name="nowPlayingButton" candidateClass="UIBarButtonItem"/>
1093 </relationships>
1094 </class>
1095 </classes>1013 </classes>
1096 <simulatedMetricsContainer key="defaultSimulatedMetrics">1014 <simulatedMetricsContainer key="defaultSimulatedMetrics">
1097 <simulatedStatusBarMetrics key="statusBar"/>1015 <simulatedStatusBarMetrics key="statusBar"/>
@@ -1100,6 +1018,6 @@
1100 </simulatedMetricsContainer>1018 </simulatedMetricsContainer>
1101 <inferredMetricsTieBreakers>1019 <inferredMetricsTieBreakers>
1102 <segue reference="jMd-4K-zoU"/>1020 <segue reference="jMd-4K-zoU"/>
1103 <segue reference="e8X-Xp-0ra"/>1021 <segue reference="ebj-wD-yft"/>
1104 </inferredMetricsTieBreakers>1022 </inferredMetricsTieBreakers>
1105</document>1023</document>
1106\ No newline at end of file1024\ No newline at end of file
11071025
=== modified file 'Music/Utilities/UOPlayer.h'
--- Music/Utilities/UOPlayer.h 2013-02-11 04:30:25 +0000
+++ Music/Utilities/UOPlayer.h 2013-02-11 04:30:25 +0000
@@ -45,4 +45,6 @@
45@property (nonatomic) BOOL shuffle;45@property (nonatomic) BOOL shuffle;
46@property (nonatomic) RepeatState repeat;46@property (nonatomic) RepeatState repeat;
4747
48- (double)streamerProgress;
49
48@end50@end
4951
=== modified file 'Music/Utilities/UOPlayer.m'
--- Music/Utilities/UOPlayer.m 2013-02-11 04:30:25 +0000
+++ Music/Utilities/UOPlayer.m 2013-02-11 04:30:25 +0000
@@ -106,7 +106,7 @@
106106
107#pragma mark - Player state107#pragma mark - Player state
108108
109- (double)progress {109- (double)streamerProgress {
110 return streamer.progress;110 return streamer.progress;
111}111}
112112
113113
=== modified file 'Music/View Controllers/AlbumViewController.h'
--- Music/View Controllers/AlbumViewController.h 2013-01-24 15:46:16 +0000
+++ Music/View Controllers/AlbumViewController.h 2013-02-11 04:30:25 +0000
@@ -12,6 +12,7 @@
1212
13@interface AlbumViewController : UIViewController13@interface AlbumViewController : UIViewController
14@property (nonatomic, retain) NSString *albumId;14@property (nonatomic, retain) NSString *albumId;
15@property (nonatomic, retain) NSString *artistId;
1516
16@property (strong, nonatomic) IBOutlet UIBarButtonItem *nowPlayingButton;17@property (strong, nonatomic) IBOutlet UIBarButtonItem *nowPlayingButton;
17@end18@end
1819
=== modified file 'Music/View Controllers/AlbumViewController.m'
--- Music/View Controllers/AlbumViewController.m 2013-02-11 04:30:25 +0000
+++ Music/View Controllers/AlbumViewController.m 2013-02-11 04:30:25 +0000
@@ -20,6 +20,7 @@
20 NSArray *tableData;20 NSArray *tableData;
21 21
22 Album *album;22 Album *album;
23 Artist *artist;
23}24}
24@property (weak, nonatomic) IBOutlet UIImageView *albumArt;25@property (weak, nonatomic) IBOutlet UIImageView *albumArt;
25@property (weak, nonatomic) IBOutlet UILabel *albumTitle;26@property (weak, nonatomic) IBOutlet UILabel *albumTitle;
@@ -30,6 +31,7 @@
3031
31@implementation AlbumViewController32@implementation AlbumViewController
32@synthesize albumId = _albumId;33@synthesize albumId = _albumId;
34@synthesize artistId = _artistId;
3335
34- (void)viewDidLoad {36- (void)viewDidLoad {
35 [self.tableView setDataSource:self];37 [self.tableView setDataSource:self];
@@ -58,7 +60,11 @@
58 60
59 if ([album.songs count]) {61 if ([album.songs count]) {
60 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"track" ascending:YES];62 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"track" ascending:YES];
61 tableData = [album.songs sortedArrayUsingDescriptors:@[sortDescriptor]];63 if (self.artistId) {
64 tableData = [[album songsForArtistId:self.artistId] sortedArrayUsingDescriptors:@[sortDescriptor]];
65 } else {
66 tableData = [album.songs sortedArrayUsingDescriptors:@[sortDescriptor]];
67 }
62 [self.tableView reloadData];68 [self.tableView reloadData];
63 } else {69 } else {
64 [[UOWebServiceController controller] updateSongs:self];70 [[UOWebServiceController controller] updateSongs:self];
@@ -116,7 +122,11 @@
116 [managedObjectContext refreshObject:album mergeChanges:NO];122 [managedObjectContext refreshObject:album mergeChanges:NO];
117 123
118 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"track" ascending:YES];124 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"track" ascending:YES];
119 tableData = [album.songs sortedArrayUsingDescriptors:@[sortDescriptor]];125 if (self.artistId) {
126 tableData = [[album songsForArtistId:self.artistId] sortedArrayUsingDescriptors:@[sortDescriptor]];
127 } else {
128 tableData = [album.songs sortedArrayUsingDescriptors:@[sortDescriptor]];
129 }
120 130
121 [self.tableView reloadData];131 [self.tableView reloadData];
122}132}
123133
=== modified file 'Music/View Controllers/AlbumsViewController.m'
--- Music/View Controllers/AlbumsViewController.m 2013-01-24 16:37:09 +0000
+++ Music/View Controllers/AlbumsViewController.m 2013-02-11 04:30:25 +0000
@@ -46,6 +46,10 @@
46 [[UOWebServiceController controller] updateAlbums:self];46 [[UOWebServiceController controller] updateAlbums:self];
47}47}
4848
49- (void)refresh {
50 [[UOWebServiceController controller] updateAlbums:self clearCache:YES];
51}
52
49#pragma mark - Storyboard methods53#pragma mark - Storyboard methods
5054
51- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {55- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
@@ -55,6 +59,7 @@
55 AlbumViewController *albumViewController = [segue destinationViewController];59 AlbumViewController *albumViewController = [segue destinationViewController];
56 Album *album = ((AlbumCell *)sender).album;60 Album *album = ((AlbumCell *)sender).album;
57 albumViewController.albumId = album.albumId;61 albumViewController.albumId = album.albumId;
62 albumViewController.artistId = nil;
58}63}
5964
60@end65@end
6166
=== modified file 'Music/View Controllers/ArtistViewController.m'
--- Music/View Controllers/ArtistViewController.m 2013-02-11 04:30:25 +0000
+++ Music/View Controllers/ArtistViewController.m 2013-02-11 04:30:25 +0000
@@ -59,11 +59,7 @@
59 self.navigationItem.rightBarButtonItem = nil;59 self.navigationItem.rightBarButtonItem = nil;
60 }60 }
61 61
62 NSString *pluralizedNoun = @"albums";62 [self setAlbumCount];
63 if ([artist.albums count] == 1) {
64 pluralizedNoun = @"album";
65 }
66 [_artistDescription setText:[NSString stringWithFormat:@"%d %@", [artist.albums count], pluralizedNoun]];
67 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"year" ascending:NO];63 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"year" ascending:NO];
68 tableData = [artist.albums sortedArrayUsingDescriptors:@[sortDescriptor]];64 tableData = [artist.albums sortedArrayUsingDescriptors:@[sortDescriptor]];
69 [self.tableView reloadData];65 [self.tableView reloadData];
@@ -98,6 +94,7 @@
98 AlbumViewController *albumViewController = [segue destinationViewController];94 AlbumViewController *albumViewController = [segue destinationViewController];
99 Album *album = ((AlbumCell *)sender).album;95 Album *album = ((AlbumCell *)sender).album;
100 albumViewController.albumId = album.albumId;96 albumViewController.albumId = album.albumId;
97 albumViewController.artistId = self.artistId;
101}98}
10299
103#pragma mark - UOWebServiceDelegate methods100#pragma mark - UOWebServiceDelegate methods
@@ -110,6 +107,7 @@
110 tableData = [artist.albums sortedArrayUsingDescriptors:@[sortDescriptor]];107 tableData = [artist.albums sortedArrayUsingDescriptors:@[sortDescriptor]];
111 108
112 [self.tableView reloadData];109 [self.tableView reloadData];
110 [self setAlbumCount];
113}111}
114112
115- (void)webserviceUpdateError:(NSError *)error {113- (void)webserviceUpdateError:(NSError *)error {
@@ -139,4 +137,12 @@
139 artist = [results objectAtIndex:0];137 artist = [results objectAtIndex:0];
140}138}
141139
140- (void)setAlbumCount {
141 NSString *pluralizedNoun = @"albums";
142 if ([artist.albums count] == 1) {
143 pluralizedNoun = @"album";
144 }
145 [_artistDescription setText:[NSString stringWithFormat:@"%d %@", [artist.albums count], pluralizedNoun]];
146}
147
142@end148@end
143149
=== modified file 'Music/View Controllers/ArtistsViewController.m'
--- Music/View Controllers/ArtistsViewController.m 2013-01-24 15:46:16 +0000
+++ Music/View Controllers/ArtistsViewController.m 2013-02-11 04:30:25 +0000
@@ -49,6 +49,10 @@
49 [[UOWebServiceController controller] updateArtists:self];49 [[UOWebServiceController controller] updateArtists:self];
50}50}
5151
52- (void)refresh {
53 [[UOWebServiceController controller] updateArtists:self clearCache:YES];
54}
55
52#pragma mark - Storyboard methods56#pragma mark - Storyboard methods
5357
54- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {58- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
5559
=== modified file 'Music/View Controllers/PlayerViewController.m'
--- Music/View Controllers/PlayerViewController.m 2013-02-11 04:30:25 +0000
+++ Music/View Controllers/PlayerViewController.m 2013-02-11 04:30:25 +0000
@@ -185,8 +185,8 @@
185}185}
186186
187- (void)heartbeat:(NSTimer*)timer {187- (void)heartbeat:(NSTimer*)timer {
188 NSInteger progress = (NSInteger)[[UOPlayer player] progress];188 double progress = [[UOPlayer player] streamerProgress];
189 self.elapsedTime.text = [NSString clockStringFromSeconds:progress];189 self.elapsedTime.text = [NSString clockStringFromSeconds:(int)progress];
190 self.progressView.value = progress;190 self.progressView.value = progress;
191}191}
192192
193193
=== modified file 'Music/View Controllers/PlaylistsViewController.m'
--- Music/View Controllers/PlaylistsViewController.m 2013-01-22 19:50:43 +0000
+++ Music/View Controllers/PlaylistsViewController.m 2013-02-11 04:30:25 +0000
@@ -45,4 +45,8 @@
45 [[UOWebServiceController controller] updatePlaylists:self];45 [[UOWebServiceController controller] updatePlaylists:self];
46}46}
4747
48- (void)refresh {
49 [[UOWebServiceController controller] updatePlaylists:self clearCache:YES];
50}
51
48@end52@end
4953
=== modified file 'Music/View Controllers/SongsViewController.m'
--- Music/View Controllers/SongsViewController.m 2013-02-11 04:30:25 +0000
+++ Music/View Controllers/SongsViewController.m 2013-02-11 04:30:25 +0000
@@ -9,7 +9,7 @@
9#import "SongsViewController.h"9#import "SongsViewController.h"
10#import "UONetworkStatusCoordinator.h"10#import "UONetworkStatusCoordinator.h"
11#import "Song.h"11#import "Song.h"
12#import "SongCell.h"12#import "SongListCell.h"
13#import "NSString+Extras.h"13#import "NSString+Extras.h"
14#import "UOWebServiceController.h"14#import "UOWebServiceController.h"
15#import "UOPlayer.h"15#import "UOPlayer.h"
@@ -37,23 +37,27 @@
37}37}
3838
39- (void)configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath {39- (void)configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath {
40 SongCell *songCell = (SongCell *)cell;40 SongListCell *songCell = (SongListCell *)cell;
41 Song *song = [self.fetchedResultsController objectAtIndexPath:indexPath];41 Song *song = [self.fetchedResultsController objectAtIndexPath:indexPath];
42 songCell.showArt = YES;
43 [songCell setSong:song];42 [songCell setSong:song];
44}43}
4544
46- (void)viewWillAppear:(BOOL)animated {45- (void)update {
47 [super viewWillAppear:animated];
48 [[UOWebServiceController controller] updateSongs:self];46 [[UOWebServiceController controller] updateSongs:self];
49}47}
5048
49- (void)refresh {
50 [[UOWebServiceController controller] updateSongs:self clearCache:YES];
51}
52
53#pragma mark - UIStoryboard methods
54
51- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {55- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
52 NSArray *songs = [[self.fetchedResultsController fetchedObjects] copy];56 NSArray *songs = [[self.fetchedResultsController fetchedObjects] copy];
53 if (![sender respondsToSelector:@selector(song)]) {57 if (![sender respondsToSelector:@selector(song)]) {
54 return; // Hit 'Now Playing'58 return; // Hit 'Now Playing'
55 }59 }
56 Song *song = ((SongCell *)sender).song;60 Song *song = ((SongListCell *)sender).song;
57 NSUInteger index = [songs indexOfObject:song];61 NSUInteger index = [songs indexOfObject:song];
58 62
59 UOPlayer *player = [UOPlayer player];63 UOPlayer *player = [UOPlayer player];
6064
=== modified file 'Music/View Controllers/UOIndexedViewController.h'
--- Music/View Controllers/UOIndexedViewController.h 2013-01-24 15:46:16 +0000
+++ Music/View Controllers/UOIndexedViewController.h 2013-02-11 04:30:25 +0000
@@ -23,6 +23,7 @@
23@property (readonly, nonatomic) NSString *lastUpdatedKey;23@property (readonly, nonatomic) NSString *lastUpdatedKey;
24@property (readonly, nonatomic) NSArray *sortDescriptors;24@property (readonly, nonatomic) NSArray *sortDescriptors;
25- (void)update;25- (void)update;
26- (void)refresh;
26- (void)configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath;27- (void)configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath;
2728
28- (NSFetchedResultsController *)fetchedResultsController;29- (NSFetchedResultsController *)fetchedResultsController;
2930
=== modified file 'Music/View Controllers/UOIndexedViewController.m'
--- Music/View Controllers/UOIndexedViewController.m 2013-02-11 04:30:25 +0000
+++ Music/View Controllers/UOIndexedViewController.m 2013-02-11 04:30:25 +0000
@@ -10,11 +10,12 @@
10#import <RestKit/RestKit.h>10#import <RestKit/RestKit.h>
11#import "UOAppDelegate.h"11#import "UOAppDelegate.h"
12#import "UOPlayer.h"12#import "UOPlayer.h"
13#import "SSPullToRefresh.h"
1314
14@interface UOIndexedViewController () {15@interface UOIndexedViewController () <SSPullToRefreshViewDelegate> {
15 NSFetchedResultsController *_fetchedResultsController;16 NSFetchedResultsController *_fetchedResultsController;
16}17}
1718@property (nonatomic, strong) SSPullToRefreshView *pullToRefreshView;
18@end19@end
1920
20@implementation UOIndexedViewController21@implementation UOIndexedViewController
@@ -48,13 +49,24 @@
48- (void)update {49- (void)update {
49}50}
5051
52- (void)refresh {
53}
54
51#pragma mark - UIViewController lifecycle55#pragma mark - UIViewController lifecycle
5256
53- (void)viewDidLoad {57- (void)viewDidLoad {
54 [super viewDidLoad];58 [super viewDidLoad];
59
60 self.pullToRefreshView = [[SSPullToRefreshView alloc] initWithScrollView:self.tableView delegate:self];
61 self.pullToRefreshView.contentView = [[SSPullToRefreshSimpleContentView alloc] initWithFrame:CGRectZero];
55 groupedTableData = [NSMutableDictionary dictionary];62 groupedTableData = [NSMutableDictionary dictionary];
56}63}
5764
65- (void)viewDidUnload {
66 [super viewDidUnload];
67 self.pullToRefreshView = nil;
68}
69
58- (void)viewWillAppear:(BOOL)animated {70- (void)viewWillAppear:(BOOL)animated {
59 [super viewWillAppear:animated];71 [super viewWillAppear:animated];
60 [self update];72 [self update];
@@ -66,12 +78,6 @@
66 }78 }
67}79}
6880
69- (void)didReceiveMemoryWarning
70{
71 [super didReceiveMemoryWarning];
72 // Dispose of any resources that can be recreated.
73}
74
75#pragma mark - Table view data source81#pragma mark - Table view data source
7682
77- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {83- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
@@ -114,25 +120,6 @@
114 NSLog(@"Error: %@", [error localizedDescription]);120 NSLog(@"Error: %@", [error localizedDescription]);
115}121}
116122
117#pragma mark - RKObjectLoaderDelegate methods
118/*
119- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error {
120 [UONetworkStatusCoordinator removeNetworkActivity];
121 if ([error code] == 2) { // Network is down.
122 return;
123 }
124 NSLog(@"Error: %@", [error localizedDescription]);
125 [[NSUserDefaults standardUserDefaults] setObject:nil forKey:[self lastUpdatedKey]];
126}
127
128- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
129 [UONetworkStatusCoordinator removeNetworkActivity];
130 _fetchedResultsController = nil;
131 [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:[self lastUpdatedKey]];
132 [self.tableView reloadData];
133}
134 */
135
136#pragma mark - Fetched results controller123#pragma mark - Fetched results controller
137124
138- (NSFetchedResultsController *)fetchedResultsController {125- (NSFetchedResultsController *)fetchedResultsController {
@@ -166,4 +153,13 @@
166 [self.tableView reloadData];153 [self.tableView reloadData];
167}154}
168155
156#pragma mark - SSPullToRefreshViewDelegate methods
157
158- (void)pullToRefreshViewDidStartLoading:(SSPullToRefreshView *)view {
159 [self.pullToRefreshView startLoading];
160 [self refresh];
161 [self.pullToRefreshView finishLoading];
162}
163
164
169@end165@end
170166
=== modified file 'Music/Views/Table Cells/ArtistCell.m'
--- Music/Views/Table Cells/ArtistCell.m 2013-01-28 00:40:22 +0000
+++ Music/Views/Table Cells/ArtistCell.m 2013-02-11 04:30:25 +0000
@@ -32,6 +32,8 @@
32 pluralizedNoun = @"album";32 pluralizedNoun = @"album";
33 }33 }
34 [self.detailTextLabel setText:[NSString stringWithFormat:@"%d %@", [artist.albums count], pluralizedNoun]];34 [self.detailTextLabel setText:[NSString stringWithFormat:@"%d %@", [artist.albums count], pluralizedNoun]];
35 } else {
36 [self.detailTextLabel setText:@" "];
35 }37 }
36}38}
3739
3840
=== added file 'Music/Views/Table Cells/SongListCell.h'
--- Music/Views/Table Cells/SongListCell.h 1970-01-01 00:00:00 +0000
+++ Music/Views/Table Cells/SongListCell.h 2013-02-11 04:30:25 +0000
@@ -0,0 +1,15 @@
1//
2// SongListCell.h
3// U1Music
4//
5// Created by Paul Hummer on 2/6/13.
6// Copyright (c) 2013 Canonical. All rights reserved.
7//
8
9#import <UIKit/UIKit.h>
10
11@class Song;
12
13@interface SongListCell : UITableViewCell
14@property (nonatomic, retain) Song *song;
15@end
016
=== added file 'Music/Views/Table Cells/SongListCell.m'
--- Music/Views/Table Cells/SongListCell.m 1970-01-01 00:00:00 +0000
+++ Music/Views/Table Cells/SongListCell.m 2013-02-11 04:30:25 +0000
@@ -0,0 +1,34 @@
1//
2// SongListCell.m
3// U1Music
4//
5// Created by Paul Hummer on 2/6/13.
6// Copyright (c) 2013 Canonical. All rights reserved.
7//
8
9#import "SongListCell.h"
10#import "UODownloader.h"
11#import "Song.h"
12#import "Artist.h"
13
14@implementation SongListCell
15@synthesize song;
16
17- (void)setSong:(Song *)_song {
18 song = _song;
19
20 if (!song.art) {
21 [self.imageView setImage:[UIImage imageNamed:@"default-album-art-120.png"]];
22 [[UODownloader downloader] downloadArt:song.artUrl toPath:song.artPath completionBlock:^(UIImage *image){
23 [self.imageView setImage:image];
24 [self setNeedsDisplay];
25 }];
26 } else {
27 [self.imageView setImage:song.art];
28 }
29
30 [self.textLabel setText:song.title];
31 [self.detailTextLabel setText:song.artist.name];
32}
33
34@end
035
=== modified file 'U1Music.xcodeproj/project.pbxproj'
--- U1Music.xcodeproj/project.pbxproj 2013-02-11 04:30:25 +0000
+++ U1Music.xcodeproj/project.pbxproj 2013-02-11 04:30:25 +0000
@@ -28,6 +28,10 @@
28 523B3CFA15B73BA0004394F4 /* download-grey@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF615B73BA0004394F4 /* download-grey@2x.png */; };28 523B3CFA15B73BA0004394F4 /* download-grey@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF615B73BA0004394F4 /* download-grey@2x.png */; };
29 523B3CFB15B73BA0004394F4 /* download.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF715B73BA0004394F4 /* download.png */; };29 523B3CFB15B73BA0004394F4 /* download.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF715B73BA0004394F4 /* download.png */; };
30 523B3CFC15B73BA0004394F4 /* download@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF815B73BA0004394F4 /* download@2x.png */; };30 523B3CFC15B73BA0004394F4 /* download@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF815B73BA0004394F4 /* download@2x.png */; };
31 5257414B16C37A0A00530CCC /* SongListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257414A16C37A0A00530CCC /* SongListCell.m */; };
32 5257415416C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257414F16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m */; };
33 5257415516C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257415116C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m */; };
34 5257415616C37E1A00530CCC /* SSPullToRefreshView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257415316C37E1A00530CCC /* SSPullToRefreshView.m */; };
31 5257417716C5CC5D00530CCC /* NSString+UbuntuOne.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257417616C5CC5D00530CCC /* NSString+UbuntuOne.m */; };35 5257417716C5CC5D00530CCC /* NSString+UbuntuOne.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257417616C5CC5D00530CCC /* NSString+UbuntuOne.m */; };
32 5257417A16C5CDA900530CCC /* UIImageView+UbuntuOne.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257417916C5CDA900530CCC /* UIImageView+UbuntuOne.m */; };36 5257417A16C5CDA900530CCC /* UIImageView+UbuntuOne.m in Sources */ = {isa = PBXBuildFile; fileRef = 5257417916C5CDA900530CCC /* UIImageView+UbuntuOne.m */; };
33 5257416D16C5653100530CCC /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5257416C16C5653100530CCC /* Crashlytics.framework */; };37 5257416D16C5653100530CCC /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5257416C16C5653100530CCC /* Crashlytics.framework */; };
@@ -274,6 +278,15 @@
274 523B3CF615B73BA0004394F4 /* download-grey@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "download-grey@2x.png"; sourceTree = "<group>"; };278 523B3CF615B73BA0004394F4 /* download-grey@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "download-grey@2x.png"; sourceTree = "<group>"; };
275 523B3CF715B73BA0004394F4 /* download.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = download.png; sourceTree = "<group>"; };279 523B3CF715B73BA0004394F4 /* download.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = download.png; sourceTree = "<group>"; };
276 523B3CF815B73BA0004394F4 /* download@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "download@2x.png"; sourceTree = "<group>"; };280 523B3CF815B73BA0004394F4 /* download@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "download@2x.png"; sourceTree = "<group>"; };
281 5257414916C37A0A00530CCC /* SongListCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SongListCell.h; sourceTree = "<group>"; };
282 5257414A16C37A0A00530CCC /* SongListCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SongListCell.m; sourceTree = "<group>"; };
283 5257414D16C37E1A00530CCC /* SSPullToRefresh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSPullToRefresh.h; sourceTree = "<group>"; };
284 5257414E16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSPullToRefreshDefaultContentView.h; sourceTree = "<group>"; };
285 5257414F16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSPullToRefreshDefaultContentView.m; sourceTree = "<group>"; };
286 5257415016C37E1A00530CCC /* SSPullToRefreshSimpleContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSPullToRefreshSimpleContentView.h; sourceTree = "<group>"; };
287 5257415116C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSPullToRefreshSimpleContentView.m; sourceTree = "<group>"; };
288 5257415216C37E1A00530CCC /* SSPullToRefreshView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSPullToRefreshView.h; sourceTree = "<group>"; };
289 5257415316C37E1A00530CCC /* SSPullToRefreshView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSPullToRefreshView.m; sourceTree = "<group>"; };
277 5257417516C5CC5D00530CCC /* NSString+UbuntuOne.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+UbuntuOne.h"; path = "Categories/NSString+UbuntuOne.h"; sourceTree = "<group>"; };290 5257417516C5CC5D00530CCC /* NSString+UbuntuOne.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+UbuntuOne.h"; path = "Categories/NSString+UbuntuOne.h"; sourceTree = "<group>"; };
278 5257417616C5CC5D00530CCC /* NSString+UbuntuOne.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+UbuntuOne.m"; path = "Categories/NSString+UbuntuOne.m"; sourceTree = "<group>"; };291 5257417616C5CC5D00530CCC /* NSString+UbuntuOne.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+UbuntuOne.m"; path = "Categories/NSString+UbuntuOne.m"; sourceTree = "<group>"; };
279 5257417816C5CDA900530CCC /* UIImageView+UbuntuOne.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImageView+UbuntuOne.h"; path = "Categories/UIImageView+UbuntuOne.h"; sourceTree = "<group>"; };292 5257417816C5CDA900530CCC /* UIImageView+UbuntuOne.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImageView+UbuntuOne.h"; path = "Categories/UIImageView+UbuntuOne.h"; sourceTree = "<group>"; };
@@ -659,6 +672,7 @@
659 29B97317FDCFA39411CA2CEA /* Resources */,672 29B97317FDCFA39411CA2CEA /* Resources */,
660 964FA39013CA5BE60018A65B /* Dependencies */,673 964FA39013CA5BE60018A65B /* Dependencies */,
661 29B97323FDCFA39411CA2CEA /* Frameworks */,674 29B97323FDCFA39411CA2CEA /* Frameworks */,
675 5279764815F00B2600F8435F /* libz.dylib */,
662 19C28FACFE9D520D11CA2CBB /* Products */,676 19C28FACFE9D520D11CA2CBB /* Products */,
663 );677 );
664 name = CustomTemplate;678 name = CustomTemplate;
@@ -713,6 +727,21 @@
713 name = Categories;727 name = Categories;
714 sourceTree = "<group>";728 sourceTree = "<group>";
715 };729 };
730 5257414C16C37E1A00530CCC /* SSPullToRefresh */ = {
731 isa = PBXGroup;
732 children = (
733 5257414D16C37E1A00530CCC /* SSPullToRefresh.h */,
734 5257414E16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.h */,
735 5257414F16C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m */,
736 5257415016C37E1A00530CCC /* SSPullToRefreshSimpleContentView.h */,
737 5257415116C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m */,
738 5257415216C37E1A00530CCC /* SSPullToRefreshView.h */,
739 5257415316C37E1A00530CCC /* SSPullToRefreshView.m */,
740 );
741 name = SSPullToRefresh;
742 path = Dependencies/SSPullToRefresh;
743 sourceTree = "<group>";
744 };
716 5268508616AE516B001F65A6 /* Products */ = {745 5268508616AE516B001F65A6 /* Products */ = {
717 isa = PBXGroup;746 isa = PBXGroup;
718 children = (747 children = (
@@ -801,6 +830,8 @@
801 526850DB16AEFA7D001F65A6 /* PlaylistCell.m */,830 526850DB16AEFA7D001F65A6 /* PlaylistCell.m */,
802 526850DC16AEFA7D001F65A6 /* SongCell.h */,831 526850DC16AEFA7D001F65A6 /* SongCell.h */,
803 526850DD16AEFA7D001F65A6 /* SongCell.m */,832 526850DD16AEFA7D001F65A6 /* SongCell.m */,
833 5257414916C37A0A00530CCC /* SongListCell.h */,
834 5257414A16C37A0A00530CCC /* SongListCell.m */,
804 );835 );
805 path = "Table Cells";836 path = "Table Cells";
806 sourceTree = "<group>";837 sourceTree = "<group>";
@@ -1268,8 +1299,8 @@
1268 964FA39013CA5BE60018A65B /* Dependencies */ = {1299 964FA39013CA5BE60018A65B /* Dependencies */ = {
1269 isa = PBXGroup;1300 isa = PBXGroup;
1270 children = (1301 children = (
1302 5257414C16C37E1A00530CCC /* SSPullToRefresh */,
1271 93BC520A124C187700B7587C /* SynthesizeSingleton.h */,1303 93BC520A124C187700B7587C /* SynthesizeSingleton.h */,
1272 5279764815F00B2600F8435F /* libz.dylib */,
1273 91328278144E07EA00395F40 /* TestFlight SDK */,1304 91328278144E07EA00395F40 /* TestFlight SDK */,
1274 );1305 );
1275 name = Dependencies;1306 name = Dependencies;
@@ -1631,6 +1662,10 @@
1631 522B24EA16B4BC1E0084B023 /* UODownloadPanGestureRecognizer.m in Sources */,1662 522B24EA16B4BC1E0084B023 /* UODownloadPanGestureRecognizer.m in Sources */,
1632 520BBF2416B51F2A00307F32 /* UODownloader.m in Sources */,1663 520BBF2416B51F2A00307F32 /* UODownloader.m in Sources */,
1633 5295FD2416B7559F00A07440 /* UOPlayer.m in Sources */,1664 5295FD2416B7559F00A07440 /* UOPlayer.m in Sources */,
1665 5257414B16C37A0A00530CCC /* SongListCell.m in Sources */,
1666 5257415416C37E1A00530CCC /* SSPullToRefreshDefaultContentView.m in Sources */,
1667 5257415516C37E1A00530CCC /* SSPullToRefreshSimpleContentView.m in Sources */,
1668 5257415616C37E1A00530CCC /* SSPullToRefreshView.m in Sources */,
1634 5257417716C5CC5D00530CCC /* NSString+UbuntuOne.m in Sources */,1669 5257417716C5CC5D00530CCC /* NSString+UbuntuOne.m in Sources */,
1635 5257417A16C5CDA900530CCC /* UIImageView+UbuntuOne.m in Sources */,1670 5257417A16C5CDA900530CCC /* UIImageView+UbuntuOne.m in Sources */,
1636 );1671 );

Subscribers

People subscribed via source and target branches