Supporting iOS 15. With tis code I get a vertical cell with an image and a text, if there are notifications, I show a red round text. matter is on landscape my cell stretches too much, and notification is too far away. any solution I try if solves the iPad issue, maybe centers the red label instead putting it on right high corner.
- on iPhone 11 portrait - all ok
- on iPhone 11 landscape - notification set is a bit too distant
- on iPad 10th landscape notification is very far away
on iPhone portrait
on iPad landscape
import SwiftUI
struct FavouritesWebAppsMainView: View {
@EnvironmentObject var classFromEntryPoint: ClassFromEntryPoint
//code for presenting sheet.
@State private var selectedItem: WebAppModel?
//related to customized wkwebViews
@State private var errorMessage: String = "none"
@State private var showAlert = false
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
]
var body: some View {
VStack {
headerView
mainBodyWithGrid
.sheet(item: $selectedItem) { webApp in
let test = webApp.appUrl
if let url = URL(string: test) {
CustomWebView(url: url, messageErrorFromWebView: $errorMessage)
HStack {
Spacer()
Button(action: {
selectedItem = nil //Used to dismiss the sheet
}) {
Label("", systemImage: "xmark")
}
}
.padding()
.background(Color.gray.opacity(0.2))
} else {
//never called
let _ = self.errorMessage = "Not valid URL"
let _ = self.showAlert = true
let _ = Logger.error("failed url: \(webApp.appUrl)")
}
}
} //most external view
.backgroundImage()
.onChange(of: errorMessage, perform: { newValue in
self.showAlert = true
})
.alert("Errore", isPresented: $showAlert) {
Button("ok", role: .cancel) {}
} message: {
Text(self.errorMessage)
}
}
//MARK: single views
private var headerView: some View {
VStack {
HStack {
Text("Welvome")
.font(.largeTitle)
.foregroundColor(.blue)
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding()
Spacer()
}
Text("Le tue app")
.padding(.bottom)
}
}
private var mainBodyWithGrid: some View {
ZStack(alignment: .bottom) {
ScrollView {
if classFromEntryPoint.preferredAppList.isEmpty {
Text("Add App to the store")
} else {
LazyVGrid(columns: columns, spacing: 30) {
ForEach(classFromEntryPoint.preferredAppList) { item in
VerticalCellView(item: item)
.onTapGesture {
self.selectedItem = item
}
}
}
.padding(.horizontal)
}
}
logoutButton
}
}
private var logoutButton: some View {
HStack {
Button("Logout") {
Logger.debug("logout")
classFromEntryPoint.performLogout()
}
.padding()
.background(.red)
.foregroundStyle(.white)
.clipShape(Capsule())
.padding()
Spacer()
}
}
}
//MARK: - cell
struct VerticalCellView: View {
var item: WebAppModel
var body: some View {
ZStack(alignment: .topTrailing) {
GeometryReader { geometry in
VStack {
ImageDownloadedFromWeb(url: item.logoUrl ?? "N/D")
.frame(width: min(geometry.size.width, 100), height: min(geometry.size.width, 100))
Text(item.name)
.font(.headline)
.frame(maxWidth: .infinity)
// Text(item.description)
// .font(.subheadline)
// .lineLimit(2)
// .truncationMode(.tail)
}
.frame(width: geometry.size.width)
}
let notificationCount = (Int.random(in: 0...20))
Text(notificationCount > 10 ? "99+" : "\(notificationCount)")
.font(.system(size: 12))
.frame(width: 25, height: 25)
.background(.red)
.foregroundColor(.white)
.clipShape(Circle())
}
.frame(height: 150)
}
}
POSSIBLE SOLUTION but not clear why it is working
struct VerticalCellView: View {
var item: WebAppModel
var body: some View {
VStack {
ImageDownloadedFromWeb(url: item.logoUrl ?? "N/D")
.frame(width: 100, height: 100)
.overlay(
notificationBadge,
alignment: .topTrailing
)
Text(item.name)
.font(.headline)
// Text(item.description)
// .font(.subheadline)
// .lineLimit(2)
// .truncationMode(.tail)
}
.frame(height: 150)
}
private var notificationBadge: some View {
let notificationCount = (Int.random(in: 0...20))
return Text(notificationCount > 10 ? "99+" : "\(notificationCount)")
.font(.system(size: 12))
.frame(width: 25, height: 25)
.background(.red)
.foregroundColor(.white)
.clipShape(Circle())
.offset(x: 10, y: -10)
}
}
To follow up on the comments, I would suggest showing the badges as an overlay over the images. This way, the badges will never be detached from the images.
Here is a stripped-down version of your example to illustrate how you can do it with overlays. In the spirit of a minimal reproducible example I left out everything that wasn't needed or relevant. In particular, I found myself wondering, why you were using a
GeometryReader
at all? The size it was giving you was the size of the grid cell, but this is not particularly useful. So I think it can be solved without using aGeometryReader
.The screenshots are from an iPad 10th gen.