Статьи

Сборка музыкального проигрывателя iOS: элементы управления проигрывателем

Эта серия из трех частей научит вас, как создать собственный музыкальный проигрыватель с помощью iOS SDK. Читай дальше!



Добро пожаловать во вторую часть этого учебного пособия о создании собственного музыкального проигрывателя с помощью iOS SDK. В этой части мы продолжим раздел «Альбомы», который мы начали в первой части, и научимся воспроизводить отдельные треки.


Откройте раскадровку и перетащите контроллер табличного представления из библиотеки объектов на холст. Мы будем использовать этот контроллер табличного представления, чтобы показать один альбом. Удерживая клавишу CTRL, перетащите ячейку в контроллере табличного представления «Альбомы» на новый контроллер табличного представления, который мы только что добавили, и во всплывающем меню выберите «push» в разделе «Sege выбора».

Теперь мы собираемся создать наши ячейки. Первая ячейка в табличном представлении будет содержать некоторую информацию об альбоме, такую ​​как обложка, исполнитель альбома, продолжительность альбома и количество песен в альбоме. Другая ячейка будет содержать песни из альбома. Выберите ячейку и откройте инспектор атрибутов. Установите Идентификатор на «Ячейка» и измените Стиль с «Пользовательский» на «Субтитры».

Теперь, когда мы создали ячейку для песен, перетащите новую ячейку табличного представления из библиотеки объектов поверх ячейки, которую мы только что отредактировали. Измените его стиль выделения с «Синий» на «Нет» и установите Идентификатор на «InfoCell». После этого откройте инспектор размера и измените высоту строки на 120.

В этой ячейке нам понадобится изображение для иллюстрации и два ярлыка для информации. Итак, сначала перетащите изображение в ячейку и измените его размер на 100×100. Также измените свойства X и Y на 10. После этого перетащите две метки в ячейку и выровняйте их, как на рисунке ниже:

Выберите первую метку, откройте инспектор атрибутов, а затем измените шрифт на «System Bold 17.0». Затем удалите текст с обеих меток и измените метку с первой метки на 101, а метку со второй метки на 102. Наконец, выберите вид изображения и измените его метку на 100. Мы будем использовать эти метки для настройки метки и представление изображения в tableView:cellForRowAtIndexPath: метод.


Перейдите в «Файл»> «Новый»> «Файл …», чтобы создать новый файл. Выберите «Класс Objective-C» и нажмите «Далее». Введите «AlbumViewController» для класса и убедитесь, что это подкласс UITableViewController и что оба флажка не установлены. Нажмите «Далее» еще раз, а затем нажмите «Создать».

Откройте AlbumViewController.h и измените код следующим образом:

01
02
03
04
05
06
07
08
09
10
11
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
 
@interface AlbumViewController : UITableViewController
{
    NSString *albumTitle;
}
 
@property NSString *albumTitle;
 
@end

Здесь мы просто добавляем инфраструктуру MediaPlayer в наш контроллер табличного представления и создаем строку NSString, которая будет содержать название альбома. Мы обновим эту строку, когда пользователь выберет альбом.

Теперь откройте AlbumsViewController.m и добавьте следующую строку в #import "AlbumsViewController.h" :

1
#import «AlbumViewController.h»

После этого добавьте следующий метод:

01
02
03
04
05
06
07
08
09
10
11
12
13
— (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    AlbumViewController *detailViewController = [segue destinationViewController];
     
    MPMediaQuery *albumsQuery = [MPMediaQuery albumsQuery];
    NSArray *albums = [albumsQuery collections];
     
    int selectedIndex = [[self.tableView indexPathForSelectedRow] row];
    MPMediaItem *selectedItem = [[albums objectAtIndex:selectedIndex] representativeItem];
    NSString *albumTitle = [selectedItem valueForProperty:MPMediaItemPropertyAlbumTitle];
                     
    [detailViewController setAlbumTitle:albumTitle];
}

Здесь мы передаем название выбранного альбома в detailViewController , который является AlbumViewController. Раскадровка вызывает этот метод во время выполнения, когда вы запускаете переход в текущей сцене (т.е. когда пользователь выбирает альбом).

Теперь откройте AlbumViewController.m и добавьте следующую строку под @implementation:

1
@synthesize albumTitle;

После этого перейдите к методу viewDidLoad и измените его следующим образом:

1
2
3
4
5
6
— (void)viewDidLoad
{
    [super viewDidLoad];
     
    self.title = albumTitle;
}

Здесь мы просто устанавливаем заголовок панели навигации на название выбранного альбома.

Теперь, когда мы создали новый экран для выбранного альбома, я думаю, что это хорошая идея для тестирования приложения. Нажмите Build and Run, чтобы протестировать приложение. Если вы перейдете на вкладку «Альбомы» и выберите альбом, вы перейдете на новый экран с пустым представлением таблицы, но заголовок панели навигации должен совпадать с названием выбранного альбома.


Перейдите к numberOfSectionsInTableView: и tableView:numberOfRowsInSection: методы в AlbumViewController.m и измените их следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
 
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery];
     
    MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue: albumTitle forProperty: MPMediaItemPropertyAlbumTitle];
    [albumQuery addFilterPredicate:albumPredicate];
     
    NSArray *albumTracks = [albumQuery items];
 
    return [albumTracks count]+1;
}

Эти методы должны быть уже знакомы, но, как вы видите, мы сделали что-то новое во втором методе. Мы использовали MPMediaPropertyPredicte. С объектом MPMediaPropertyPredicte вы можете фильтровать запрос. Мы использовали название выбранного альбома для значения фильтра и использовали MPMediaPropertyAlbumTitle в качестве свойства, поэтому наш запрос будет содержать только альбом с названием альбома, выбранного пользователем.

Вы можете использовать множество различных свойств для MPMediaPropertyPredicate. Вы можете просмотреть их все в документации по iOS Developer Library .

Мы возвращаем количество песен плюс 1. Дополнительная ячейка будет для ячейки с информацией об альбоме.

Поскольку у нас есть два разных типа ячеек с двумя разными высотами, мы должны указать нашему табличному представлению, какую высоту использовать для какой ячейки. Чтобы сделать это, добавьте следующий метод под numberOfRowsInSection: метод:

1
2
3
4
5
6
7
8
— (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([indexPath row] == 0) {
        return 120;
    } else {
        return 44;
    }
}

Здесь мы сообщаем нашему табличному виду, что наша первая ячейка с информацией об альбоме имеет высоту 120 пикселей, а все остальные ячейки с песнями альбома имеют высоту 44 пикселя.

Теперь мы создадим четыре различных метода, чтобы получить некоторую информацию об альбоме. В первом методе мы получим обложку альбома выбранного альбома. Во втором методе мы получим альбом исполнителя. В третьем методе мы получим продолжительность альбома и количество песен в альбоме. И в последнем методе мы проверим, совпадают ли исполнители песен в альбоме. Иногда песни в альбоме имеют одинаковых исполнителей, но не всегда. Если в песнях используются разные исполнители, мы показываем их в виде таблицы, но если все исполнители одинаковы, мы не будем их показывать, потому что вы уже можете видеть их в первой ячейке.

Мы начнем с создания первого метода, поэтому добавьте следующий код в метод viewDidLoad :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
— (UIImage *) getAlbumArtworkWithSize: (CGSize) albumSize
{
    MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery];
    MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue: albumTitle forProperty: MPMediaItemPropertyAlbumTitle];
    [albumQuery addFilterPredicate:albumPredicate];
    NSArray *albumTracks = [albumQuery items];
     
    for (int i = 0; i < [albumTracks count]; i++) {
         
        MPMediaItem *mediaItem = [albumTracks objectAtIndex:i];
        UIImage *artworkImage;
         
        MPMediaItemArtwork *artwork = [mediaItem valueForProperty: MPMediaItemPropertyArtwork];
        artworkImage = [artwork imageWithSize: CGSizeMake (1, 1)];
         
        if (artworkImage) {
            artworkImage = [artwork imageWithSize:albumSize];
            return artworkImage;
        }
         
    }
     
    return [UIImage imageNamed:@»No-artwork-album.png»];
}

Этот метод возвращает изображение обложки альбома определенного размера. Сначала мы проверяем каждую песню, чтобы увидеть, есть ли у нее изображение. Мы делаем это с небольшим размером, поэтому наше приложение будет быстрым. Как только мы находим художественное изображение, мы снова получаем это изображение с правильным размером и возвращаем изображение. После этого методы останавливаются, поэтому, если первая песня содержит изображение обложки, она возвращает это изображение и останавливается с помощью метода. Мы добавили этот метод, потому что иногда не все песни в альбоме содержат художественные изображения. Если метод не нашел изображение обложки, мы возвращаем изображение обложки по умолчанию.

Мы еще не добавили это изображение по умолчанию, поэтому давайте сделаем это сначала. Загрузите исходный код, прикрепленный к этому проекту, и перетащите изображения [email protected] и No-artwork-album.png в проект. Убедитесь, что установлен флажок «Копировать элементы в папку целевой группы (если необходимо)» и нажмите «Готово».

Теперь добавьте следующий код для второго метода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
— (NSString *) getAlbumArtist
{
    MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery];
    MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue: albumTitle forProperty: MPMediaItemPropertyAlbumTitle];
    [albumQuery addFilterPredicate:albumPredicate];
    NSArray *albumTracks = [albumQuery items];
     
    for (int i = 0 ; i < [albumTracks count]; i++) {
         
        NSString *albumArtist = [[[albumTracks objectAtIndex:0] representativeItem] valueForProperty:MPMediaItemPropertyAlbumArtist];
         
        if (albumArtist) {
            return albumArtist;
        }
    }
 
    return @»Unknown artist»;
}

Здесь мы делаем то же, что и в предыдущем методе, но на этот раз для исполнителя альбома. Мы проверяем, есть ли у каждой песни исполнитель альбома. Если он есть, мы возвращаем его, и метод останавливается. Если метод не находит исполнителя альбома, он возвращает строку «Неизвестный исполнитель».

Теперь добавьте следующий код для нашего третьего метода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
— (NSString *) getAlbumInfo
{
    MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery];
    MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue: albumTitle forProperty: MPMediaItemPropertyAlbumTitle];
    [albumQuery addFilterPredicate:albumPredicate];
    NSArray *albumTracks = [albumQuery items];
     
     
    NSString *trackCount;
     
    if ([albumTracks count] > 1) {
        trackCount = [NSString stringWithFormat:@»%i Songs», [albumTracks count]];
    } else {
        trackCount = [NSString stringWithFormat:@»1 Song»];
    }
     
    long playbackDuration = 0;
     
    for (MPMediaItem *track in albumTracks)
    {
        playbackDuration += [[track valueForProperty:MPMediaItemPropertyPlaybackDuration] longValue];
    }
     
    int albumMimutes = (playbackDuration /60);
    NSString *albumDuration;
     
    if (albumMimutes > 1) {
        albumDuration = [NSString stringWithFormat:@»%i Mins.», albumMimutes];
    } else {
        albumDuration = [NSString stringWithFormat:@»1 Min.»];
    }
     
    return [NSString stringWithFormat:@»%@, %@», trackCount, albumDuration];
 
}

В этом методе мы создаем строку с информацией о количестве песен в выбранном альбоме и о продолжительности альбома. Сначала мы создаем наш запрос и добавляем фильтр для названия альбома. Затем мы создаем массив с элементами из запроса. Затем мы проверяем, есть ли в альбоме одна или несколько песен. Если у него есть одна песня, мы устанавливаем строку trackCount на «1 Song», или же мы устанавливаем эту строку на количество песен. После этого мы создаем переменную для продолжительности альбома. Мы получаем длительность в секундах, но поскольку мы хотим показать их в минутах, мы делим переменную playDuration на 60. Как только это будет сделано, мы проверяем, больше ли альбом, чем 1 минута. Если это правда, мы устанавливаем строку продолжительности альбома на количество минут, иначе мы говорим, что она равна 1 минуте. Наконец, мы возвращаем строку с количеством дорожек и продолжительностью альбома.

Теперь, когда мы создали наши первые три метода, добавьте следующий код для последнего метода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
— (BOOL) sameArtists
{
    MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery];
    MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue: albumTitle forProperty: MPMediaItemPropertyAlbumTitle];
    [albumQuery addFilterPredicate:albumPredicate];
    NSArray *albumTracks = [albumQuery items];
     
    for (int i = 0 ; i < [albumTracks count]; i++) {
         
        if ([[[[albumTracks objectAtIndex:0] representativeItem] valueForProperty:MPMediaItemPropertyArtist] isEqualToString:[[[albumTracks objectAtIndex:i] representativeItem] valueForProperty:MPMediaItemPropertyArtist]]) {
        } else {
            return NO;
        }
    }
     
    return YES;
}

В этом методе мы проверяем, являются ли исполнители песен одинаковыми или нет. Он использует цикл for, чтобы проверить, совпадает ли исполнитель первой песни с исполнителем из текущего номера цикла. Если значения Artist не равны, метод возвращает логическое NO, но если он завершен с циклом, и ни один из артистов не совпадает, методы возвращают логическое YES.

Это было много кода, но это даст нашему приложению лучший опыт, так что оно того стоит. Чтобы фактически показать информацию об альбоме и песнях, нам нужно изменить метод tableView: cellForRowAtIndexPath: Перейдите к этому методу и измените код следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([indexPath row] == 0) {
        static NSString *CellIdentifier = @»InfoCell»;
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
         
        UIImageView *albumArtworkImageView = (UIImageView *)[cell viewWithTag:100];
        albumArtworkImageView.image = [self getAlbumArtworkWithSize:albumArtworkImageView.frame.size];
         
        UILabel *albumArtistLabel = (UILabel *)[cell viewWithTag:101];
        albumArtistLabel.text = [self getAlbumArtist];
         
        UILabel *albumInfoLabel = (UILabel *)[cell viewWithTag:102];
        albumInfoLabel.text = [self getAlbumInfo];
         
        return cell;
    } else {
         
        static NSString *CellIdentifier = @»Cell»;
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
         
         
        MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery];
        MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue: albumTitle forProperty: MPMediaItemPropertyAlbumTitle];
        [albumQuery addFilterPredicate:albumPredicate];
        NSArray *albumTracks = [albumQuery items];
         
        NSUInteger trackNumber = [[[albumTracks objectAtIndex:(indexPath.row-1)] valueForProperty:MPMediaItemPropertyAlbumTrackNumber] unsignedIntegerValue];
         
        if (trackNumber) {
            cell.textLabel.text = [NSString stringWithFormat:@»%i. %@», trackNumber, [[[albumTracks objectAtIndex:(indexPath.row-1)] representativeItem] valueForProperty:MPMediaItemPropertyTitle]];
        } else {
            cell.textLabel.text = [[[albumTracks objectAtIndex:(indexPath.row-1)] representativeItem] valueForProperty:MPMediaItemPropertyTitle];
        }
 
         
        if ([self sameArtists]) {
     
            cell.detailTextLabel.text = @»»;
             
        } else {
             
            if ([[[albumTracks objectAtIndex:(indexPath.row-1)] representativeItem] valueForProperty:MPMediaItemPropertyArtist]) {
                cell.detailTextLabel.text = [[[albumTracks objectAtIndex:(indexPath.row-1)] representativeItem] valueForProperty:MPMediaItemPropertyArtist];
            } else {
                cell.detailTextLabel.text = @»»;
            }
             
        }
 
        return cell;
    }
}

Это очень большой метод, но код не очень сложный. Начнем с самого начала.

Начните с проверки, какую строку мы используем. Если мы используем первую строку, мы хотим показать первую ячейку, которую мы создали в нашей раскадровке. Как вы можете видеть, мы используем тот же CellIdentifier, который мы использовали для ячейки в нашей раскадровке. Мы создали UIImageView и присвоили его представлению изображения в ячейке с тегом 100, который, конечно, такой же, как мы использовали ранее. После этого мы вызываем метод getAlbumArtworkWithSize: который мы создали ранее, чтобы получить обложку альбома и обновить представление изображения для этой обложки. Мы использовали размер представления изображения для размера нашего произведения искусства. После этого мы делаем то же самое для двух ярлыков. Для текста первой метки мы вызываем метод getAlbumArtist а для второй метки мы вызываем метод getAlbumInfo .

Когда мы используем не первый, а другой ряд, мы хотим показать песни альбома. Здесь мы сначала создаем запрос и добавляем фильтр к этому запросу, а затем сохраняем элементы этого запроса в массиве. После этого мы получаем номер трека в альбоме и проверяем, есть ли он. Если имеется номер трека, мы добавляем его перед названием песни или просто показываем название песни. Как видите, мы использовали (indexpath.row-1) для индекса дорожек. Мы делаем это, потому что первая ячейка табличного представления используется для информации об альбоме.

После этого мы проверяем, имеют ли песни тех же исполнителей, вызывая метод sameArtists который мы создали ранее. Если исполнители совпадают, мы удаляем текст элемента detailTextLabel ячейки, но если исполнители отличаются и дорожка содержит исполнителя, мы устанавливаем текст элемента detailTextLabel для исполнителя этой дорожки.

Наконец, нам нужно обновить класс нашего контроллера табличного представления, поэтому откройте раскадровку, выберите контроллер табличного представления, который мы создали в этом уроке, откройте Identity Inspector и измените класс на «AlbumViewController».

Теперь, когда мы заполнили наши ячейки информацией и песнями, я думаю, что сейчас самое время протестировать наше приложение. Нажмите Build and Run, чтобы протестировать приложение. Если вы перейдете на вкладку «Альбомы» и выберите альбом, вы должны увидеть табличное представление с изображением альбома, некоторую информацию об альбоме и, конечно, песни, которые есть в этом альбоме.


Теперь, когда мы создали классное приложение, которое показывает наши песни и альбомы, было бы неплохо, если бы наш музыкальный проигрыватель действительно мог воспроизводить эти песни и альбомы. Откройте раскадровку и перетащите контроллер вида из библиотеки объектов на холст. Мы собираемся использовать этот View Controller в следующем уроке, чтобы показать уже воспроизводимую песню, но нам уже нужны переходы для воспроизведения треков. Перетащите CTRL со второй ячейки в контроллере табличного представления, которую мы создали в этом руководстве, и выберите «нажать» в разделе «Выбор перехода» во всплывающем меню. Теперь сделайте то же самое для ячейки в контроллере представления таблицы композиций.

Откройте SongsViewController.m и добавьте следующий метод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
— (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    MPMediaQuery *songsQuery = [MPMediaQuery songsQuery];
    NSArray *songs = [songsQuery items];
     
    int selectedIndex = [[self.tableView indexPathForSelectedRow] row];
     
    MPMediaItem *selectedItem = [[songs objectAtIndex:selectedIndex] representativeItem];
     
    MPMusicPlayerController *musicPlayer = [MPMusicPlayerController iPodMusicPlayer];
     
    [musicPlayer setQueueWithItemCollection:[MPMediaItemCollection collectionWithItems:[songsQuery items]]];
    [musicPlayer setNowPlayingItem:selectedItem];
     
    [musicPlayer play];
}

Мы использовали этот метод ранее в этом уроке, но на этот раз мы используем его для воспроизведения выбранной дорожки. Сначала мы создаем запрос для наших песен и помещаем элементы в массив. Затем мы получаем MPMediaItem из созданного нами массива. Мы использовали тот же индекс, что и выбранная строка, поэтому мы можем использовать этот MPMediaItem позже в этом методе для обновления воспроизводимого элемента. После этого мы создаем объект MPMusicPlayerController и устанавливаем его в iPodMusicPlayer. Это означает, что наше приложение разделяет состояние iPod, и если мы закроем наше приложение, музыка продолжит играть. Затем мы обновляем очередь музыкального проигрывателя на элементы запроса и устанавливаем элемент, который сейчас воспроизводится, на MPMediaItem, который мы создали ранее в этом методе. Наконец-то мы начинаем играть музыку.

Теперь откройте AlbumViewController.m и добавьте следующий метод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
— (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery];
    MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue: albumTitle forProperty: MPMediaItemPropertyAlbumTitle];
    [albumQuery addFilterPredicate:albumPredicate];
    NSArray *albumTracks = [albumQuery items];
     
    int selectedIndex = [[self.tableView indexPathForSelectedRow] row];
     
    MPMediaItem *selectedItem = [[albumTracks objectAtIndex:selectedIndex-1] representativeItem];
     
    MPMusicPlayerController *musicPlayer = [MPMusicPlayerController iPodMusicPlayer];
     
    [musicPlayer setQueueWithItemCollection:[MPMediaItemCollection collectionWithItems:[albumQuery items]]];
    [musicPlayer setNowPlayingItem:selectedItem];
     
    [musicPlayer play];
}

Здесь мы фактически делаем то же самое, что и в методе, который мы создали в SongsViewController.m, но мы устанавливаем очередь музыкального проигрывателя для выбранного альбома.

Теперь наше приложение может воспроизводить песни, нажмите Build & Run, чтобы протестировать это приложение!


Во второй части серии, посвященной созданию музыкального проигрывателя, мы рассмотрели, как создать пользовательскую ячейку для табличного представления с раскадровкой, как использовать MPMediaPropertyPredicate и как воспроизводить песни и альбомы, которые мы показываем в нашем приложении. В следующей и последней части серии мы создадим экран, который воспроизводится сейчас, и создадим собственный дизайн интерфейса для нашего приложения.