Reputation: 23
I want to display a list of images. Each image have an associated text, and I want to display that text like a "popover" in style (but how it is achieved is not important - it doesn't have to be as a popover "call"), i.e. on top of the image but only as large as the text actually is, the image is supposed to be seen still in the background around the text.
I have the images in a HStack
or VStack
, depending on how the user holds the device and I have a button which I want to use to trigger showing the text. I have tried various ways of adding .popover
modifier in various places without managing to show anything, including the Button
. But I haven't gotten it to work in any way.
I'm fairly inexperienced, so the answer might be obvious!
The GrannyStreamDataItem
data structure uses named images at the moment, but that will of course change later on.
How do I trigger and show the text when the user taps the i
-button?
//Data structure
struct GrannyStreamDataItem: Identifiable {
var id = UUID()
var image: String
var comment: String = ""
var commentPosition: Alignment = .bottomLeading
}
let grannyStreamData: [GrannyStreamDataItem] = [
GrannyStreamDataItem(image: "Pisa1", comment: "Leaning tower of Pisa. Gosh, it is crowded!", commentPosition: .topLeading),
GrannyStreamDataItem(image: "Pisa2", comment: "This is what the tower looks like from the inside :-)", commentPosition: .topTrailing),
]
// View showing images
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
if geometry.size.width > geometry.size.height {
ScrollView(.horizontal) {
VStack {
Spacer()
HStack {
ImageViews(dataItemArray: grannyStreamData)
}
Spacer()
}
}
} else {
ScrollView(.vertical) {
HStack {
Spacer()
VStack {
ImageViews(dataItemArray: grannyStreamData)
}
Spacer()
}
}
}
}
}
}
struct ImageViews: View {
let dataItemArray: [GrannyStreamDataItem]
var body: some View {
ForEach(dataItemArray) { dataItem in
ZStack(alignment:dataItem.commentPosition) {
Image(dataItem.image)
.resizable()
.aspectRatio(contentMode: .fit)
Button {
showTextImage(description: dataItem.comment)
} label: {
Image(systemName: "info.circle")
.padding(.all, 10)
.font(.largeTitle)
.foregroundColor(.black)
}
//.popover() here won't work... Why not?
}
}
}
func showTextImage(description: String) {
print("Image text: \(description)")
}
}
Upvotes: 0
Views: 463
Reputation: 2793
This is how I would do it:
I would favor a conditional view to a popover in this case. It has a more flexible appearance.
struct ImageList: View {
let dataItemArray: [GrannyStreamDataItem]
var body: some View {
ForEach(dataItemArray) { dataItem in
ImageView(dataItem: dataItem)
}
}
}
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
if geometry.size.width > geometry.size.height {
ScrollView(.horizontal) {
HStack {
ImageList(dataItemArray: grannyStreamData)
}
.padding(10)
}
} else {
ScrollView(.vertical) {
VStack {
ImageList(dataItemArray: grannyStreamData)
}
.padding(10)
}
}
}
}
}
struct ImageView: View {
let dataItem: GrannyStreamDataItem
@State private var showComment: Bool = false
var body: some View {
Image(dataItem.image)
.resizable()
.aspectRatio(contentMode: .fit)
.overlay(alignment: dataItem.commentPosition) {
Group {
if showComment {
Text("Image text: \(dataItem.comment)")
.padding(10)
.background(.gray.opacity(0.6))
.cornerRadius(8)
.padding(10)
} else {
Image(systemName: "info.circle")
.padding(10)
.font(.largeTitle)
.foregroundColor(.black)
}
}
.onTapGesture {
withAnimation(.easeInOut(duration: 0.3)) {
showComment.toggle()
}
}
}
}
}
I renamed ImageViews
to ImageList
to avoid any name confusion later on.
I removed excessive HStack
s, VStack
s and Spacer
s from contentView
, they also are more trouble than use.
I moved Image
and Text
to a new view ImageView
. Like that, each of them can have its own @State
variable showComment
. This variable can be toggled via an onTapGesture
and the info image or the comment text will conditionally show. Note the if
-condition needs to be enclosed by a Group
view to accept the onTapGesture
modifier. This Group is enclosed in a overlay
modifier to stack over the image with the specified alignment.
I also added an animation on the state change to make it a little smoother.
Upvotes: 0