TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

How can I force an overlay to appear over other views in a stack?

Forums > SwiftUI

I'm trying to make a histogram, with a tooltip that appears over a given bar when the user touches it. The tooltip is appearing and positioning correctly, but it sits behind some of the bars in the histogram, namely the bars drawn after the one the user is touching, i.e. bars to the right in an HStack or below in a VStack.

Example here

I have tried adding .zIndex modifiers to no effect. I have also considered applying the tooltip to the whole stack, but that involves a lot of extra work to position it correctly relative to a given bar, and I haven't quite figured that out yet.

    var histogram: some View {
            ForEach(dataPoints) { rating in
                let xScale: CGFloat = vertical ? Double(rating.value) / Double(maxValue) * scaleAmount * 0.8 : 1
                let yScale: CGFloat = vertical ? 1 : Double(rating.value) / Double(maxValue) * scaleAmount * 0.8
                let ratingIndex = dataPoints.firstIndex(of: rating)

                var tooltip: Tooltip? {
                    guard tooltipIndex != nil else { return nil }
                    guard tooltipIndex == ratingIndex else { return nil }
                    guard let formattedHistogramBarValue else { return nil }
                    return Tooltip(label: formattedHistogramBarValue, cornerRadius: 5)
                }

                GeometryReader { geo in
                    ZStack {
                        VStack {
                            Spacer()
                            Rectangle()
                                .fill(color)
                                .frame(width: geo.size.width * xScale,
                                       height: geo.size.height * yScale)
                                .overlay(alignment: .top) { tooltip }
                        }
                        Rectangle()
                            .hidden()
                            .contentShape(Rectangle())
                            .onLongPressGesture(minimumDuration: .infinity) { }
                            onPressingChanged: { pressing in
                                if pressing {
                                    tooltipIndex = ratingIndex
                                } else { tooltipIndex = nil }
                            }
                    }
                }
            }
            .onAppear {
                withAnimation(.easeInOut(duration: 0.5)) {
                    scaleAmount = 1
                }
                prepareHaptics()
            }
            .onChange(of: tooltipIndex) { newValue in
                quickTapHaptic()
            }
    }

2      

@Bnerd  

Hi, your views need be on the same ZStack, then you apply .zIndex(10) to the one you want to come on the top. (I used 10 just to make sure it will be on the top, provided that you don't have 11 views on top of each other on your ZStack)

2      

Ah, so that's another approach I tried. My problem is that the tooltip popup is at least sometimes wider than the bar it's attached to, and placing them in the same ZStack seems to make the bars either move or rescale in somewhat unpredictable ways to try to match the size/positioning of the tooltip.

2      

Hi,

Try adding a zIndex property to your ratings then you could change it in the onPressingChange and put the .zIndex(rating.zIndex) in the geometryReader.

GeometryReader { geo in
    ZStack {
        VStack {
            Spacer()
            Rectangle()
                .fill(.blue)
                .frame(width: geo.size.width * xScale,
                       height: geo.size.height * yScale)
                .overlay(alignment: .top) { tooltip }
        }
        Rectangle()
            .hidden()
            .contentShape(Rectangle())
            .onLongPressGesture(minimumDuration: .infinity) { }
        onPressingChanged: { pressing in
            if pressing {
                tooltipIndex = ratingIndex
                rating.zIndex = 1
            } else {
                tooltipIndex = nil
                rating.zIndex = 0
            }
        }
    }

}
.zIndex(rating.zIndex)

2      

Hacking with Swift is sponsored by String Catalog.

SPONSORED Get accurate app localizations in minutes using AI. Choose your languages & receive translations for 40+ markets!

Localize My App

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.