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      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

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.