I'm looking for an solution to display my artwork like in the apple music application. I'm able to load one artwork for the playlist but I want to be able to show 4 of the artworks as a playlist representative.
Currently I'm using this code for my playlist view
let objects = items.objectAtIndex(indexPath.item) as! MPMediaItemCollection
let repObjects = objects.representativeItem
cell.lbl_Name.text = objects.valueForProperty(MPMediaPlaylistPropertyName) as? String
let artwork = repObjects?.valueForProperty(MPMediaItemPropertyArtwork)
let artworkImage = artwork?.imageWithSize(CGSize(width: 130, height: 130))
if(artworkImage != nil){
cell.img_artwork.image = artworkImage
}
else{
cell.img_artwork.image = UIImage(named: "no_Artwork.png")
}
What would be the best way to go at this
Related
Apple announced lossless audio for apple music users in 2021. With MediaPlayer and MusicKit, you can also play any song from the users apple music library. This playback works through a MPMediaItem, you can query one like this:
#State private var librarySongs = [MPMediaItem]()
#State private var libraryPlaylists = [MPMediaItemCollection]()
let songsQuery = MPMediaQuery.songs()
if let songs = songsQuery.items {
let desc = NSSortDescriptor(key: MPMediaItemPropertyDateAdded, ascending: false)
let sortedSongs = NSArray(array: songs).sortedArray(using: [desc])
librarySongs = sortedSongs as? [MPMediaItem] ?? []
}
let playlistQuery = MPMediaQuery.playlists()
if let playlists = playlistQuery.collections {
libraryPlaylists = playlists
}
MPMediaItem documentation
Now to my question, is it possible to play a users music library as lossless audio?
I want to share a post on facebook with Image and text. I am using Facebook SDK and I am able to share Image, Now I want to share text with this image. How I can?
Here is the source code of sharing the image,
let photo:FBSDKSharePhoto = FBSDKSharePhoto()
var message:String?
let imageName:String = "Star.png"
photo.image = UIImage(named: imageName)
photo.isUserGenerated = true
let content = FBSDKSharePhotoContent()
content.photos = [photo];
let dialog = FBSDKShareDialog()
dialog.fromViewController = self
dialog.shareContent = content
dialog.mode = .native
dialog.show()
Due to the abuse from some applications, Facebook has explicitly disabled the pre-fill option for the text by simply ignoring it.
"You must not pre-fill any of the fields associated with the
following products, unless the user manually generated the content
earlier in the workflow: Stream stories (user_message parameter for
Facebook.streamPublish and FB.Connect.streamPublish, and message
parameter for stream.publish), Photos (caption), Videos (description),
Notes (title and content), Links (comment), and Jabber/XMPP."
More info here.
Facebook wont allow user to add text with image.
Integrate facebookshare sdk
#IBAction func facebookBtn(sender: AnyObject) {
let shareImage = SharePhoto()
shareImage.image = imageView.image //Image from your imageview
shareImage.isUserGenerated = true
let content = SharePhotoContent()
content.photos = [shareImage]
let sharedDialoge = ShareDialog()
sharedDialoge.shareContent = content
sharedDialoge.fromViewController = self
sharedDialoge.mode = .automatic
if(sharedDialoge.canShow)
{
sharedDialoge.show()
}
else
{
print("Install Facebook client app to share image")
}
}
Try this :
let sharePhoto = FBSDKSharePhoto()
photo.image = photo
photo.caption = "you title"
let content = FBSDKShareMediaContent()
content.media = [photo];
I am trying to make a music player and currently I am stuck on filtering and prepending an album to the playlist.
What I am trying to do is play music on shuffle and then when a user taps a button continue playing songs only from the album currently playing, when it is finished I want to know its finished so I can change UI elements and then go on to play any and all music from the library.
However, what happens is it will play the rest of the songs from the album THEN when it has exhausted all the songs from that album will play a random song then after that one random song will go back to that album ..go through its entirety and then play random songs. Once in a blue moon after it finishes the album it will just play random songs.
In a singleton I have
func getAllSongs(completion: #escaping (_ songs: [MPMediaItem]?) -> Void) {
MPMediaLibrary.requestAuthorization { (status) in
if status == .authorized {
let query = MPMediaQuery()
let mediaTypeMusic = MPMediaType.music
let audioFilter = MPMediaPropertyPredicate(value: mediaTypeMusic.rawValue, forProperty: MPMediaItemPropertyMediaType, comparisonType: MPMediaPredicateComparison.equalTo)
query.addFilterPredicate(audioFilter)
let songs = query.items
completion(songs)
} else {
completion(nil)
}
}
}
func getSongsWithCurrentAlbumFor(item: MPMediaItem) -> MPMediaQuery {
let albumFilter = MPMediaPropertyPredicate(value: item.albumTitle, forProperty: MPMediaItemPropertyAlbumTitle, comparisonType: MPMediaPredicateComparison.equalTo)
let predicates: Set<MPMediaPropertyPredicate> = [albumFilter]
let query = MPMediaQuery(filterPredicates: predicates)
query.addFilterPredicate(albumFilter)
return query
}
In my VC to set up the audio I use
let mediaPlayer = MPMusicPlayerApplicationController.applicationMusicPlayer
func setUpAudioPlayerAndGetSongsShuffled() {
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
try? AVAudioSession.sharedInstance().setActive(true)
MBProgressHUD.showAdded(to: view, animated: true)
MediaManager.shared.getAllSongs { (songs) in
guard let theSongs = songs else {
return
}
self.newSongs = theSongs.filter({ (item) -> Bool in
return !self.playedSongs.contains(item)
})
self.mediaPlayer.setQueue(with: MPMediaItemCollection(items: self.newSongs))
self.mediaPlayer.shuffleMode = .songs
self.mediaPlayer.repeatMode = .none
self.mediaPlayer.prepareToPlay(completionHandler: { (error) in
DispatchQueue.main.async {
MBProgressHUD.hide(for: self.view, animated: true)
}
})
}
}
When the user taps the button to continue playing songs only from that album I use
if let nowPlaying = mediaPlayer.nowPlayingItem {
let albumQuery = MediaManager.shared.getSongsWithCurrentAlbumFor(item: nowPlaying)
print("\(albumQuery)")
let descriptor = MPMusicPlayerMediaItemQueueDescriptor(query: albumQuery)
mediaPlayer.prepend(descriptor)
}
Upon rereading the documentation I notice it says that I should change to
let mediaPlayer = MPMusicPlayerApplicationController. applicationQueuePlayer
I cannot figure out how to know when an album has been exhausted and then continue playing the rest of the music library.
it would be great to know if the album does not have any other items so that the user could not press the button to play more from the album
I discovered my mistakes. I was putting my logic in the wrong place
In my singleton I added
func hasPlayedAllSongsFromAlbumFor(song: MPMediaItem) -> Bool {
if let allSongsInAlbum = getSongsWithCurrentAlbumFor(item: song).items {
return lockedSongsContains(songs: allSongsInAlbum)
}
return true
}
func lockedSongsContains(songs: [MPMediaItem]) -> Bool {
for aSong in songs {
if !lockedSongs.contains(aSong) {
return false
}
}
return true
}
I needed to use a Notification
I created a func that has a notification in the ViewDidLoad called songChanged
In the songChanged func I have
if albumIsLocked && MediaManager.shared.hasPlayedAllSongsFromAlbumFor(song: nowPlaying) {
albumLockButtonTapped(albumLockIconButton)
MediaManager.shared.lockedSongs.removeAll()
}
On the album lock button I now use
if let nowPlaying = mediaPlayer.nowPlayingItem {
if sender.isSelected {
sender.isSelected = false
albumIsLocked = false
let lockRemovedQuery = MediaManager.shared.removeAlbumLockFor(item: nowPlaying)
let lockRemovedDescriptor = MPMusicPlayerMediaItemQueueDescriptor(query: lockRemovedQuery)
mediaPlayer.prepend(lockRemovedDescriptor)
mediaPlayer.prepareToPlay()
} else {
sender.isSelected = true
albumIsLocked = true
let albumQuery = MediaManager.shared.getSongsWithCurrentAlbumFor(item: nowPlaying)
let albumDescriptor = MPMusicPlayerMediaItemQueueDescriptor(query: albumQuery)
mediaPlayer.prepend(albumDescriptor)
mediaPlayer.prepareToPlay()
}
}
I have a UISlider added to my app and I am trying to figure out how to change the volume of the music playing. The music playing is being pulled from the iTunes Library using MPMusicPlayerController
Here is the viewDidLoad function with to show how I get the music from the iTunes Music library.
var musicPlayer = MPMusicPlayerController()
override func viewDidLoad() {
super.viewDidLoad()
musicPlayer = MPMusicPlayerController.systemMusicPlayer()
//Changing the Play/Pause Button
if musicPlayer.playbackState == MPMusicPlaybackState.Playing {
playPauseButton.setImage(UIImage(named: "Pause"), forState: UIControlState.Normal)
//Artwork
let currentItem: MPMediaItem = musicPlayer.nowPlayingItem!
let artwork: MPMediaItemArtwork = currentItem.valueForProperty(MPMediaItemPropertyArtwork) as! MPMediaItemArtwork
let artworkImage = artwork.imageWithSize(CGSize(width: self.view.frame.width, height: 372.0))
musicPlayerArtwork.image = artworkImage
musicBottomBackground.image = artworkImage
//Titel
let titleString: String = currentItem.valueForProperty(MPMediaItemPropertyTitle) as! String
songTitle.text = titleString as String
//Artist & Album
let artistString: String = currentItem.valueForProperty(MPMediaItemPropertyArtist) as! String
let albumString: String = currentItem.valueForProperty(MPMediaItemPropertyAlbumTitle) as! String
albumAndArtistTitle.text = "\(artistString) - \(albumString)"
} else {
playPauseButton.setImage(UIImage(named: "Play"), forState: UIControlState.Normal)
}
}
// I have IBAction functions linked to controlling the playbackk and pause functions that look like this one to play the music
func startPlayMusic() {
if self.musicPlayer.playbackState == MPMusicPlaybackState.Paused {
self.musicPlayer.play()
}
}
I have the slider hooked up with an IBAction and IBOutlet and have the following function to attempt adjusting the volume it:
var audioPlayer = AVAudioPlayer()
#IBAction func volumeSliderChanging(sender: UISlider) {
audioPlayer.volume = sender.value
}
Each time I try running this and moving the slider I get the following error
Any ideas what I could be doing wrong?
Your audioPlayer was not properly initiated. You should not call AVAudioPlayer(). Refer to the documentation for list of initializers that you should use. Here's an example:
let url = NSBundle.mainBundle().URLForResource("mySong", withExtension: "mp3")!
self.audioPlayer = try! AVAudioPlayer(contentsOfURL: url)
I have managed to create an up next queuing system that works (almost) perfectly. I can move, delete and add songs to the list without any issues at all. The only problem i am having is that when the user decides to use a shuffle mode (e.g. MPMusicShuffleMode.Songs), the up next view still displays the old order (assumes shuffle mode is set to off). The app Ecoute has a similar queuing system but it also works when shuffling.
I've realized that Ecoute does not use the MPMusicShuffleMode, if you go into the stock music player after pressing shuffle in Ecoute, it stays the same. I've figured out a way to display the correct order for shuffled songs (just shuffle the NSMutableArray and set this as the new queue). The real problem is how would i shuffle this array when the user wants to 'Shuffle Albums'? The NSMutableArray contains MPMediaItem if that helps.
Many thanks.
EDIT:
This attempt works but the time grows exponentially for the number of songs. 2000 songs took about 25-35 seconds, so very slow.
else if(self.shuffleMode == "Songs"){
self.shuffleMode = "Albums"
self.shuffle.setTitle("Shuffle Albums", forState: UIControlState.Normal)
var queueCopy = NSMutableArray(array: self.queue.items)
var newQueue = NSMutableArray(array: [])
var albums = MPMediaQuery.albumsQuery().collections as [MPMediaItemCollection]
var albumsCopy = NSMutableArray(array: albums)
shuffleArray(albumsCopy)
for album in albumsCopy{
for item in queueCopy{
if (album.representativeItem!.valueForProperty(MPMediaItemPropertyAlbumTitle) as NSString == item.valueForProperty(MPMediaItemPropertyAlbumTitle) as NSString){
for(var i = 0; i < album.items!.count; i++){
if(album.items![i].valueForProperty(MPMediaItemPropertyTitle) as NSString == item.valueForProperty(MPMediaItemPropertyTitle) as NSString){
newQueue.addObject(item as MPMediaItem)
}
}
}
}
}
let newQueueToSet = MPMediaItemCollection(items: newQueue)
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
appDelegate.currentQueue = newQueueToSet
self.queue = newQueueToSet
self.player.setQueueWithItemCollection(newQueueToSet)
}
EDIT 2:
Managed to get it down to 8 seconds but it's nowhere near Ecoute's 1 to 2 seconds.
8 second version:
var albumNames = [String]()
for item in queueCopy{
albumNames.append(item.valueForProperty(MPMediaItemPropertyAlbumTitle) as NSString)
}
let unique = NSSet(array: albumNames).allObjects as [MPMediaItem]
var uniqueAlbumNames = NSMutableArray(array: unique)
shuffleArray(uniqueAlbumNames)
for name in uniqueAlbumNames{
for item in queueCopy{
if item.valueForProperty(MPMediaItemPropertyAlbumTitle) as NSString == name as NSString{
newQueue.addObject(item as MPMediaItem)
}
}
}
Final Edit:
final piece of code that i'm sticking with. takes about 7-8 seconds for nearly 3000 songs.
#IBAction func shufflePressed(sender: AnyObject) {
if self.shuffleMode == "Off"{
self.shuffleMode = "Songs"
self.shuffle.setTitle("Shuffle All", forState: UIControlState.Normal)
self.shuffle.setTitle("Shuffling", forState: UIControlState.Highlighted)
var queueCopy = NSMutableArray(array: self.queue.items)
self.unshuffledQueueCopy = self.queue
shuffleArray(queueCopy)
let newQueue = MPMediaItemCollection(items: queueCopy)
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
appDelegate.currentQueue = newQueue
self.queue = newQueue
self.player.setQueueWithItemCollection(newQueue)
}
else if(self.shuffleMode == "Songs"){
self.shuffleMode = "Albums"
self.shuffle.setTitle("Shuffle Albums", forState: UIControlState.Normal)
var queueCopy = NSMutableArray(array: self.queue.items)
var newQueue = NSMutableArray(array: [])
var albums = MPMediaQuery.albumsQuery().collections as [MPMediaItemCollection]
var albumsCopy = NSMutableArray(array: albums)
shuffleArray(albumsCopy)
// Takes 8 seconds for 3000 songs
var albumNames = [String]()
for item in queueCopy{
albumNames.append(item.valueForProperty(MPMediaItemPropertyAlbumTitle) as NSString)
}
let unique = NSSet(array: albumNames).allObjects as [MPMediaItem]
var uniqueAlbumNames = NSMutableArray(array: unique)
shuffleArray(uniqueAlbumNames)
for name in uniqueAlbumNames{
for item in queueCopy{
if item.valueForProperty(MPMediaItemPropertyAlbumTitle) as NSString == name as NSString{
newQueue.addObject(item as MPMediaItem)
}
}
}
let newQueueToSet = MPMediaItemCollection(items: newQueue)
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
appDelegate.currentQueue = newQueueToSet
self.queue = newQueueToSet
self.player.setQueueWithItemCollection(newQueueToSet)
}
else if(self.shuffleMode == "Albums"){
self.shuffleMode = "Off"
self.shuffle.setTitle("Shuffle", forState: UIControlState.Normal)
var queueCopy = NSMutableArray(array: self.unshuffledQueueCopy.items)
var nowPlayingItem = self.player.nowPlayingItem
for(var i = 0; i < queueCopy.count; i++){
if(queueCopy[i] as MPMediaItem == nowPlayingItem){
self.fromIndex = i
}
}
let newQueue = MPMediaItemCollection(items: queueCopy)
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
appDelegate.currentQueue = newQueue
self.queue = newQueue
self.player.setQueueWithItemCollection(newQueue)
self.fromItem = newQueue.items[fromIndex + 1] as MPMediaItem
}
So in pseudo code it would look something like the following.
Loop the Array making a new array of the albums only
Shuffle the new Albums array
Loop the new albums array, looking through the original array for items in that album.
On match, write those songs into a third array which will become your new playlist.
Thanks