swiftmacosswiftui

How to position a view so that its center is at a corner of another view?


I am trying to add "accessory buttons" to a view. These buttons would appear when I hover over that view with a mouse pointer. The center of these buttons should coincide with a corner of the view.

Here is my attempt at positioning a button at the top trailing corner of a rectangle

struct ContentView: View {

    @State private var hovering = false
    
    var body: some View {
        Rectangle()
            .fill(.gray)
            .frame(width: 100, height: 100)
            .overlay(alignment: .topTrailing) {
                if hovering {
                    Button { } label: {
                        Image(systemName: "eye.slash")
                    }
                    .alignmentGuide(HorizontalAlignment.trailing) { $0[HorizontalAlignment.center] }
                    .alignmentGuide(VerticalAlignment.top) { $0[VerticalAlignment.center] }
                }
            }
            .controlSize(.small)
            .buttonBorderShape(.circle)
            .onHover {
                hovering = $0
            }
    }
}

This produces

enter image description here

But the expected result is

enter image description here

I had thought that the alignmentGuides I added to the button would shift the center of the button to the top trailing corner of the rectangle, but they didn't do anything at all.

How do I achieve the desired appearance without hardcoding magic numbers for offsetting/padding the views?


Solution

  • It turns out alignmentGuides do work, if they are modifying the if hovering statement. Obviously you cannot modify an if statement directly, so you have to wrap it with a Group first.

    .overlay(alignment: .topTrailing) {
        Group {
            if hovering {
                Button { } label: {
                    Image(systemName: "eye.slash")
                }
            }
        }
        .alignmentGuide(HorizontalAlignment.trailing) { $0[HorizontalAlignment.center] }
        .alignmentGuide(VerticalAlignment.top) { $0[VerticalAlignment.center] }
    }