UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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 Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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.