今回の内容
完成形
簡単機能説明
-
UITextField
に入力されたアーティスト名
からCDを検索し、UICollectionView
に表示します。 - 表示された
UICollectionViewCell
を選択すると、CDに入っている曲名を表示するMusicDetailView
をmodal
で表示します。 - 表示された
MusicDetailView
内のUITableView
に曲名が表示されているので、選択するとVideoListView
をmodal
で表示します。 -
VideoListView
にアーティスト名と曲名
でYoutubeから取得してきた動画の一覧を複数表示します。 - 表示された一覧を選択すると動画を表示する為の
VideoPlayView
をmodal
で表示します。 - 表示された
VideoPlayView
で動画を再生します。
コード
Model
VideoDetailGroup
VideoDetailDatas
struct VideoDetailDatas{
let thumbnailImageURL:String?
let title:String?
let channelTitle:String?
let videoPlayerContents:VideoPlayerContents?
}
struct VideoPlayerContents{
let videoId:String?
let description:String?
}
VideoAlamofireProcess
import Alamofire
import SwiftyJSON
class VideoAlamofireProcess{
private var videoDetailResultArray = [VideoDetailDatas]()
}
extension VideoAlamofireProcess{
public func getVideoDetailData(searchKeyword:String?,comletion: @escaping ([VideoDetailDatas]?,Error?) -> Void){
print(searchKeyword as Any)
guard let keyword = searchKeyword else { return }
let apiKey = "https://www.googleapis.com/youtube/v3/search?&key=作成したAPIKey&part=snippet&q=\(keyword.urlEncoded)&maxResults=15"
AF.request(apiKey, method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON {[self] response in
switch response.result{
case .success:
let detailData = JSON(response.data as Any)
videoDetailResultArray = []
for dataCount in 0..<detailData["items"].count{
if let getThumbnail = detailData["items"][dataCount]["snippet"]["thumbnails"]["medium"]["url"].string,
let getTitle = detailData["items"][dataCount]["snippet"]["title"].string,
let getChannelName = detailData["items"][dataCount]["snippet"]["channelTitle"].string,
let getVideoID = detailData["items"][dataCount]["id"]["videoId"].string,
let getDiscription = detailData["items"][dataCount]["snippet"]["description"].string{
if detailData["items"][dataCount]["id"]["channelId"].string != getChannelName{
videoDetailResultArray.append(VideoDetailDatas(thumbnailImageURL: getThumbnail,
title: getTitle,
channelTitle: getChannelName,
videoPlayerContents: VideoPlayerContents(videoId: getVideoID,
description: getDiscription)))
}
}
}
comletion(videoDetailResultArray, nil)
case .failure(let error):
comletion(nil, error)
}
}
}
}
MusicDetailDataGroup
MusicDetailData
struct MusicDetailData{
let mediumImageUrl:String?
let title:String?
let artistName:String?
let playList:PlayListDetail?
}
struct PlayListDetail{
let playListContents:String?
}
MusicAlamofireProcess
import Alamofire
import SwiftyJSON
class MusicAlamofireProcess{
private var privateMusicDetailData = [MusicDetailData]()
}
extension MusicAlamofireProcess{
public func getMusicDetailData(searchKeyWord:String?,completion: @escaping ([MusicDetailData]?,Error?) -> Void){
guard let keyword = searchKeyWord else { return }
let apiKey = "https://app.rakuten.co.jp/services/api/BooksCD/Search/20170404?format=json&artistName=\(keyword.urlEncoded)&applicationId=作成したアプリID"
AF.request(apiKey, method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON {[self] response in
switch response.result{
case .success:
let detailData = JSON(response.data as Any)
privateMusicDetailData = []
for dataCount in 0..<detailData["Items"].count{
if let getMediumImageUrl = detailData["Items"][dataCount]["Item"]["mediumImageUrl"].string,
let getTitle = detailData["Items"][dataCount]["Item"]["title"].string,
let getArtistName = detailData["Items"][dataCount]["Item"]["artistName"].string,
let getPlayList = detailData["Items"][dataCount]["Item"]["playList"].string{
privateMusicDetailData.append(MusicDetailData(mediumImageUrl: getMediumImageUrl,
title: getTitle,
artistName: getArtistName,
playList: PlayListDetail(playListContents: getPlayList)))
}
}
completion(privateMusicDetailData, nil)
case .failure(let error):
completion(nil, error)
}
}
}
}
extension String{
var urlEncoded:String{
let charset = CharacterSet.alphanumerics.union(.init(charactersIn: "/?-._~"))
let remove = removingPercentEncoding ?? self
return remove.addingPercentEncoding(withAllowedCharacters: charset) ?? remove
}
}
View
Main.storyboard

CollectionViewCustomCellGroup

CollectionViewCustomCell
import UIKit
class CollectionViewCustomCell: UICollectionViewCell {
@IBOutlet weak var mediumImageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var artistNameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func prepareForReuse() {
super.prepareForReuse()
mediumImageView.image = UIImage(named: "")
titleLabel.text = ""
artistNameLabel.text = ""
}
}
MusicDetailViewGroup

MusicDetailView!
import UIKit
import SDWebImage
class MusicDetailView: UIViewController {
@IBOutlet weak var mediumImageView: UIImageView!
@IBOutlet weak var playListTableVew: UITableView!
public var playListCellContentsArray = [String]()
public var artistName = String()
public var mediumImageURL = String()
override func viewDidLoad() {
super.viewDidLoad()
playListTableVew.register(UINib(nibName: "PlayListTableViewCell", bundle: nil), forCellReuseIdentifier: "PlayListCell")
playListTableVew.delegate = self
playListTableVew.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
mediumImageView.sd_setImage(with: URL(string: mediumImageURL), completed: nil)
}
}
extension MusicDetailView:UITableViewDelegate{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.frame.height / 8
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let videoListView = VideoListView()
videoListView.getArtistName = artistName + " " + playListCellContentsArray[indexPath.row]
videoListView.modalPresentationStyle = .automatic
present(videoListView, animated: true, completion: nil)
}
}
extension MusicDetailView:UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return playListCellContentsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlayListCell", for: indexPath) as! PlayListTableViewCell
cell.playListContentsLabel.text = "\(String(indexPath.row + 1))曲目. \(playListCellContentsArray[indexPath.row])"
return cell
}
}
PlayListTableViewCell

import UIKit
class PlayListTableViewCell: UITableViewCell{
@IBOutlet weak var playListContentsLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func prepareForReuse() {
super.prepareForReuse()
playListContentsLabel.text = ""
}
}
VideoListViewGroup

VideoListView
import UIKit
class VideoListView: UIViewController {
@IBOutlet weak var videoListTableView: UITableView!
private let videoAlamofireProcess = VideoAlamofireProcess()
private var videoListContantArray = [VideoDetailDatas]()
public var getArtistName = String()
override func viewDidLoad() {
super.viewDidLoad()
videoListTableView.register(UINib(nibName: "VideoListCell", bundle: nil), forCellReuseIdentifier: "VideoListCell")
videoListTableView.delegate = self
videoListTableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
videoAlamofireProcess.getVideoDetailData(searchKeyword: getArtistName) {[self] result, error in
if error != nil{
return
}
showAnimation(showContent: "5", animationTime: 4, targetView: view) {[self] judgeBool in
if judgeBool == false{
return
}
videoListContantArray = []
videoListContantArray = result!
videoListTableView.reloadData()
}
}
}
}
extension VideoListView:UITableViewDelegate{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.frame.height / 2.2
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let videoPlayView = VideoPlayView()
videoPlayView.playVideoID = videoListContantArray[indexPath.row].videoPlayerContents?.videoId
videoPlayView.playVideoTitle = videoListContantArray[indexPath.row].title
videoPlayView.playVideoDescription = videoListContantArray[indexPath.row].videoPlayerContents?.description
videoPlayView.modalPresentationStyle = .automatic
videoPlayView.presentationController?.delegate = self
present(videoPlayView, animated: true, completion: nil)
}
}
extension VideoListView:UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videoListContantArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "VideoListCell", for: indexPath) as! VideoListCell
cell.thumbnailImageView.sd_setImage(with: URL(string: videoListContantArray[indexPath.row].thumbnailImageURL!), completed: nil)
cell.titleLabel.text = videoListContantArray[indexPath.row].title
cell.channelNameLabel.text = videoListContantArray[indexPath.row].channelTitle
return cell
}
}
extension VideoListView:UIAdaptivePresentationControllerDelegate{
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
let viewPlayView = VideoPlayView()
viewPlayView.videoView.removeFromSuperview()
}
}
VideoListCell

VideoListCell
import UIKit
class VideoListCell: UITableViewCell {
@IBOutlet weak var thumbnailImageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var channelNameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func prepareForReuse() {
super.prepareForReuse()
thumbnailImageView.image = UIImage(named: "")
titleLabel.text = ""
channelNameLabel.text = ""
}
}
VideoPlayViewGroup

import UIKit
import youtube_ios_player_helper
class VideoPlayView: UIViewController{
@IBOutlet weak var videoOnView: UIView!
@IBOutlet weak var videoTitleLabel: UILabel!
@IBOutlet weak var videoDescriptionView: UITextView!
public var videoView = YTPlayerView()
public var playVideoID:String?
public var playVideoTitle:String?
public var playVideoDescription:String?
override func viewDidLoad() {
super.viewDidLoad()
videoTitleLabel.text = playVideoTitle
videoDescriptionView.text = playVideoDescription
videoView.frame = videoOnView.frame
videoView.load(withVideoId: playVideoID!, playerVars: ["playersinline":1])
videoOnView.addSubview(videoView)
videoView.delegate = self
}
}
extension VideoPlayView:YTPlayerViewDelegate{
func playerViewDidBecomeReady(_ playerView: YTPlayerView) {
videoView.playVideo()
}
}
extension VideoPlayView{
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
super.dismiss(animated: flag, completion: completion)
guard let presentationController = presentationController else { return }
presentationController.delegate?.presentationControllerDidDismiss?(presentationController)
}
}
Lottie
import UIKit
import Lottie
extension UIViewController{
func showAnimation(showContent:String?,animationTime:Double?,targetView:UIView,completion: @escaping (Bool) -> Void){
guard let content = showContent else { completion(false); return }
guard let time = animationTime else { completion(false); return }
let underView = UIView(frame: CGRect(x: targetView.frame.maxX / 4, y: targetView.frame.maxY * 0.25, width: targetView.frame.width / 2, height: targetView.frame.width / 2))
underView.layer.cornerRadius = 15.0
underView.backgroundColor = .darkGray
underView.alpha = 0.7
targetView.addSubview(underView)
let animationView = AnimationView()
animationView.frame = CGRect(x: targetView.frame.maxX / 4, y: targetView.frame.maxY * 0.25, width: targetView.frame.width / 2, height: targetView.frame.width / 2)
animationView.layer.cornerRadius = 15.0
animationView.backgroundColor = .clear
animationView.animation = Animation.named(content)
animationView.contentMode = .scaleAspectFit
animationView.loopMode = .loop
animationView.play(completion: nil)
targetView.addSubview(animationView)
DispatchQueue.main.asyncAfter(deadline: .now() + time) {
completion(true)
animationView.stop()
animationView.removeFromSuperview()
underView.removeFromSuperview()
}
}
}
Controller
import UIKit
import SDWebImage
class ViewController: UIViewController {
@IBOutlet weak var searchTextField: UITextField!
@IBOutlet weak var searchButton: UIButton!
@IBOutlet weak var searchResultCollectionView: UICollectionView!
private let musicAlamofireProcess = MusicAlamofireProcess()
private var cellLayout = UICollectionViewFlowLayout()
private var cellContentsArray = [MusicDetailData]()
override func viewDidLoad() {
super.viewDidLoad()
searchResultCollectionView.register(UINib(nibName: "CollectionViewCustomCell", bundle: nil), forCellWithReuseIdentifier: "MusicDetailCell")
searchResultCollectionView.delegate = self
searchResultCollectionView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
cellLayout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
searchResultCollectionView.collectionViewLayout = cellLayout
}
@IBAction func search(_ sender: UIButton) {
musicAlamofireProcess.getMusicDetailData(searchKeyWord: searchTextField.text) {[self] result, error in
if error != nil{
return
}
showAnimation(showContent: "5", animationTime: 3.0, targetView: view) { resultBool in
if resultBool == false{
return
}
cellContentsArray = result!
searchResultCollectionView.reloadData()
}
}
}
}
extension ViewController:UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let musicDetailView = MusicDetailView()
musicDetailView.artistName = cellContentsArray[indexPath.row].artistName!
musicDetailView.playListCellContentsArray = (cellContentsArray[indexPath.row].playList?.playListContents?.components(separatedBy: "###"))!
musicDetailView.mediumImageURL = cellContentsArray[indexPath.row].mediumImageUrl!
musicDetailView.modalPresentationStyle = .automatic
present(musicDetailView, animated: true, completion: nil)
}
}
extension ViewController:UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cellContentsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MusicDetailCell", for: indexPath) as! CollectionViewCustomCell
cell.mediumImageView.sd_setImage(with: URL(string: cellContentsArray[indexPath.row].mediumImageUrl!), completed: nil)
cell.titleLabel.text = cellContentsArray[indexPath.row].title
cell.artistNameLabel.text = cellContentsArray[indexPath.row].artistName
return cell
}
}
extension ViewController:UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width / 2 - 10, height: collectionView.frame.height / 2 - 10)
}
}
終わり
ご指摘、ご質問などありましたら、コメントまでお願い致します。