Spotlighting / Highlighting a specific part of a UIView that is wrapped with a UIViewRepresentable?

86 views Asked by At

For extra context, this is a follow up question to this. Would appreciate reading that post as theres quite a bunch of context as to my initial approach and what I need to ultimately accomplish.

I basically have an entire UIView which is being wrapped to be able to be used with SwiftUI with a UIViewRepresentable. This UIView displays some shimmering effects while isLoading = true and removes these loading views and renders a bunch of UIStackView subviews once isLoading = false. As said on the linked post (on my last reply to the user that provided code examples), I need to be able to get the frame of a specific UIStackView (so as to draw a spotlight rectangle above it) but only when the UIView is done loading and the UIStackView has already been rendered on screen. Something such as layoutSubViews but from the SwiftUI part of things.

This needs to be accomplished without:

  • Having access / modifying the UIView in question since it comes from a 3rd party lib.
  • Subclassing the UIView as the class is marked as final.

In order to get the UIStackView frame, my approach was to inspect the superview of a button I set on the UIRepresentable (this superview is exactly the stackView I need), so my representable would look something like this:

struct DemoUIViewRepresentable: UIViewRepresentable {
    @Binding var frame: CGRect // binding used to communicate the frame to the View
    @Binding var model: model //all needed properties to set the UIView
    var isLoading: Bool //isLoading property that tracks the loading state
    
    func makeUIView(context: Context) -> DemoView {
        let view = DemoView()
        view.isLoading = true
        return view
    }
    
    func updateUIView(_ uiView: DemoView, context: Context) {
        uiView.buttons = model.buttons
        uiView.isLoading = isLoading
        if !isLoading {
            guard let frame = model.buttons.first?.superview?.frame else { return }
            self.frame = frame //updates the binding property so the view update progagates to the @State property on the View
        }
    }
}

Sadly, this doesnt work as the frame is grabbed before the shimmering views are actually removed and the UIStackViews are rendered on screen, so I get a bogus frame. If I do something like:

func updateUIView(_ uiView: DemoView, context: Context) {
    uiView.buttons = model.buttons
    uiView.isLoading = isLoading
    if !isLoading {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
            guard let frame = balanceModel.bottomActionButtons.first?.superview?.frame else { return }
            self.frame = frame
        }
    }
}

This works great as it gives time for the UIView to update and lay out its subviews (remove shimmer views and render the UIStackViews). But, this is super hacky and not a good practice at all.

Any other tips you may imagine to accomplish this to be able to hightlight/spotlight a specific part of a UIView that is wrapped with a UIViewRepresentable? Is my initial approach of grabbing the frame somewhat correct? Any pointers are greatly appreciated.

Thanks!

0

There are 0 answers