I keep learning swiftui in my free time and now I am making an app with API calls and a List, everything is fine until I need to make an offset of an image, I can do it but the image gets cut, here you have an image to see it.
This is what I have
This is what I want:
Why is the image cut off?
Here is my code:
PokemonItem
struct PokemonItem:View {
let pokemon: ApiNetwork.Pokemon
var body: some View {
HStack {
PokemonItemLeft(pokemon: pokemon)
Spacer()
PokemonItemRight(pokemon: pokemon)
}.frame(height: 115)
.background(colorForPokemonType(type: pokemon.details?.types.first?.type.name ?? ""))
.cornerRadius(16)
.listRowBackground(Color.clear)
.padding(.bottom, 16)
}
}
PokemonItemLeft
struct PokemonItemLeft: View {
let pokemon: ApiNetwork.Pokemon
var body: some View {
VStack(alignment: .leading) {
Image("DotsImage")
.resizable()
.scaledToFit()
.frame(width: 100)
.cornerRadius(8)
.padding(.leading, 64)
if let id = pokemon.details?.id {
Text(formatPokemonID(id))
.font(.caption)
.foregroundColor(Color.pokemonId)
.bold()
} else {
Text("ID: Unknown")
}
Text(pokemon.name.capitalized).font(.title2).foregroundColor(Color.white).bold()
}.padding(.leading, 20)
}
}
PokemonItemRight
struct PokemonItemRight: View {
let pokemon: ApiNetwork.Pokemon
var body: some View {
VStack {
AsyncImage(url: URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/\(pokemon.details?.id ?? 0).png")) { image in
image
.resizable()
.scaledToFit()
.frame(width: 130, height: 130)
.cornerRadius(8)
.padding(.trailing, 18)
.offset(y: -20)
.zIndex(1)
} placeholder: {
ProgressView()
}
}
.padding(.leading, 20)
}
}
This is why I am using ItemLeft and ItemRight
This is the function to change the background color
func colorForPokemonType(type: String) -> Color {
let cardColors: [String: Color] = [
"grass": Color(red: 139/255, green: 190/255, blue: 138/255),
"fire": Color(red: 255/255, green: 167/255, blue: 86/255),
"water": Color(red: 88/255, green: 171/255, blue: 246/255),
"poison": Color(red: 159/255, green: 110/255, blue: 151/255),
"normal": Color(red: 181/255, green: 185/255, blue: 196/255),
"bug": Color(red: 139/255, green: 214/255, blue: 116/255),
"flying": Color(red: 116/255, green: 143/255, blue: 201/255),
"electric": Color(red: 242/255, green: 203/255, blue: 85/255),
"ground": Color(red: 247/255, green: 133/255, blue: 81/255),
"fairy": Color(red: 235/255, green: 168/255, blue: 195/255),
"fighting": Color(red: 208/255, green: 65/255, blue: 100/255),
"psychic": Color(red: 234/255, green: 93/255, blue: 96/255),
"rock": Color(red: 186/255, green: 171/255, blue: 130/255),
"ghost": Color(red: 85/255, green: 106/255, blue: 174/255),
"ice": Color(red: 97/255, green: 206/255, blue: 192/255),
"dragon": Color(red: 15/255, green: 106/255, blue: 192/255)
]
return cardColors[type] ?? Color.gray
}
Here is the small problem with the first item of the list
Thanks a lot!
The reason your Pokemon is getting cropped because it's inside a HStack. HStack and VStack arranges its subviews horizontally and vertically respectively.
To fix this, think your card as layers, base layer being the background, first layer being the pokemon image. Using ZStack you can achieve required results. The reason that ZStack works is that it "overlays" its subviews.
Have written sample code below with some hardcoded values for the same.
for testing purposes
struct ContentView: View {
var body: some View {
PokemonItem()
}
}
It has 2 layers: one base layer and one content layer
struct PokemonItem: View {
var body: some View {
ZStack {
//base card
colorForPokemonType(type: "grass")
.cornerRadius(16)
//content
PokemonItemDetails()
}
.frame(height: 115)
.listRowBackground(Color.clear)
.padding(.bottom, 16)
}
}
A wrapper for content that needs to be displayed on the Card.
struct PokemonItemDetails: View {
var body: some View {
HStack {
PokemonItemLeft()
Spacer()
PokemonItemRight(id: 1)
}
}
}
struct PokemonItemLeft: View {
var body: some View {
VStack(alignment: .leading) {
Spacer()
Image("DotsImage")
.resizable()
.scaledToFit()
.frame(width: 100)
.cornerRadius(8)
.padding(.leading, 64)
Text("#001")
.font(.caption)
.foregroundColor(Color.black)
.bold()
Text("bulbasaur".capitalized)
.font(.title2)
.foregroundColor(Color.white)
.bold()
.padding(.bottom, 10)
}.padding(.leading, 20)
}
}
struct PokemonItemRight: View {
let id: Int
var body: some View {
HStack {
Spacer()
AsyncImage(url: URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/\(id).png")) { image in
image
.resizable()
.scaledToFit()
.frame(width: 130, height: 130)
.cornerRadius(8)
.padding(.trailing, 18)
.offset(y: -20)
.zIndex(1)
} placeholder: {
ProgressView()
}
}
.padding(.leading, 20)
}
}
PS: to get extact positioning of dots, you'll just need to tweak some paddings and it should work fine.
Hope this solves your query.