'How to show MPMediaItem Artwork in a SwiftUI list?
I try to build a SwiftUI List of all local stored songs on my iPhone. Im using the MediaPlayer Framework of Apple to get the songs and storing them inside an EnvironmentObject for easy access in my SwiftUI view.
Inside my Cell im accessing the image via, but all i get is a white 50x50 block:
Image(uiImage: self.item.artwork!.image(at: CGSize(width: 50, height: 50))!)
Result: https://i.imgur.com/gulvJ8u.png
// EnvironmentObject
class UserData: ObservableObject {
@Published var allowMusicLibraryAccess: Bool = false
@Published var songs: [MPMediaItem]
init() {
self.songs = [MPMediaItem]()
self.initAllowMusicLibraryAccess()
}
private func initAllowMusicLibraryAccess() -> Void {
MPMediaLibrary.requestAuthorization { status in
if status == .authorized {
DispatchQueue.main.async {
self.allowMusicLibraryAccess = true
self.songs = MPMediaQuery.songs().items!
}
}
}
}
}
// List
struct ContentView: View {
@EnvironmentObject var userData: UserData
var body: some View {
ZStack() {
if self.userData.allowMusicLibraryAccess {
NavigationView {
List {
ForEach(self.userData.songs, id: \.persistentID) { song in
SongCell(song)
}
}
.navigationBarTitle("Songs")
}
} else {
Text("Music Library Access needed")
}
}
}
}
// Cell
struct SongCell: View {
let item: MPMediaItem
init(_ item: MPMediaItem) {
self.item = item
}
var body: some View {
Button(action: {
print("clicked \(self.item)")
}) {
HStack() {
if self.item.artwork != nil {
Image(uiImage: self.item.artwork!.image(at: CGSize(width: 50, height: 50))!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
}
VStack(alignment: .leading) {
Text(self.item.title ?? "---")
Text(self.item.artist ?? "---")
.font(.system(.footnote))
.opacity(0.7)
}
}
}
.frame(minWidth: nil, idealWidth: nil, maxWidth: .infinity, minHeight: 55, idealHeight: 55, maxHeight: 55, alignment: .leading)
}
}
Solution 1:[1]
The solution is to use the .renderingMode() modifier:
Image(uiImage: self.item.artwork!.image(at: CGSize(width: 50, height: 50))!)
.renderingMode(.original)
Solution 2:[2]
With iOS 15,
We can use
ArtworkImage(artwork: self.item.artwork, width: 250) // Specify the width
Solution 3:[3]
I did figure it out, but it kinda seems like a bug ore some really interesting Initialization behavior I cant explain.
I figured out that moving the Image out of the Cell and directly into the List ForEach fixed that problem. Like I said, I cant explain why this is the case.
// Cell
struct SongCell: View {
let item: MPMediaItem
init(_ item: MPMediaItem) {
self.item = item
}
var body: some View {
Button(action: {
print("clicked \(self.item)")
}) {
VStack(alignment: .leading) {
Text(self.item.title ?? "---")
Text(self.item.artist ?? "---")
.font(.system(.footnote))
.opacity(0.7)
}
}
.frame(minWidth: nil, idealWidth: nil, maxWidth: .infinity, minHeight: 55, idealHeight: 55, maxHeight: 55, alignment: .leading)
}
}
// List
struct ContentView: View {
@EnvironmentObject var userData: UserData
var body: some View {
ZStack() {
if self.userData.allowMusicLibraryAccess {
NavigationView {
List {
ForEach(self.userData.songs, id: \.persistentID) { song in
HStack() {
Image(uiImage: song.artwork!.image(at: CGSize(width: 50, height: 50))!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
SongCell(song)
}
}
}
.navigationBarTitle("Songs")
}
} else {
Text("Music Library Access needed")
}
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Chorus Audio |
Solution 2 | Nitish Kumar |
Solution 3 | KevinP |