How to "fix" a button on a specific position in SwiftUI

101 Views Asked by At

I have a TV remote image asset complete with buttons where I lay invisible buttons on top of it to make it functional. The problem is, since the invisible buttons need to be over their respective graphic counterparts like pause, play etc. the buttons should be fixed at a certain position, regardless of the device size. I've tried GeometryReader, RemoteGeo, even some UIKit modification that ChatGPT suggested to no avail. Can someone be kind enough to help me out?

Sample buttons are below:

private func iphonePortraitButtons() -> some View {
        
        ZStack {
            VStack {
                
                // Design for Portrait Mode
                
                ////////////////DVD PAUSE BUTTON////////////////
                Button(action: { let generator = UIImpactFeedbackGenerator(style: .heavy)
                    generator.impactOccurred()
                    isDVDLogoPaused.toggle() }) {
                        Rectangle().opacity(1)
                            .foregroundColor(.green)
                    }
                    .frame(width: remoteGeo.size.width * 0.15,
                           height: remoteGeo.size.height * 0.08)
                    .position(x: remoteGeo.size.width * 0.158,
                              y: remoteGeo.size.height * 0.13)
                
                ////////////////INFO BUTTON////////////////
                Button(action: { let generator = UIImpactFeedbackGenerator(style: .heavy)
                    generator.impactOccurred()
                    showFloatingIsland.toggle() }) {
                        Rectangle().opacity(1)
                            .foregroundColor(.green)
                    }
                    .frame(width: remoteGeo.size.width * 0.16,
                           height: remoteGeo.size.height * 0.05)
                    .position(x: remoteGeo.size.width * 0.675,
                              y: remoteGeo.size.height * 0.077)
                
                //////////////////DIGITAL CLOCK BUTTON////////////////
                Button(action: { let generator = UIImpactFeedbackGenerator(style: .heavy)
                    generator.impactOccurred()
                    showDigitalClock.toggle() }) {
                        Rectangle().opacity(1)
                            .foregroundColor(.green)
                    }
                    .frame(width: remoteGeo.size.width * 0.16,
                           height: remoteGeo.size.height * 0.031)
                    .position(x: remoteGeo.size.width * 0.310,
                              y: remoteGeo.size.height * 0.095)
          
                }
               }
              }
1

There are 1 best solutions below

0
Benzy Neez On

The main problem may be that the buttons are contained in a VStack. Try removing the VStack, so that the buttons are held by the ZStack instead. Then put a GeometryReader around the ZStack to give you the size. This whole view can then be applied as an overlay to the TV image.

Here is an adaption of your example to show it working. The sizes that you were setting on some of the buttons were very small, I increased them a bit.

struct ContentView: View {
    
    private func buttonIcon(systemName: String) -> some View {
        Image(systemName: systemName)
            .resizable()
            .scaledToFit()
            .foregroundStyle(Color(white: 0.2, opacity: 0.2))
    }
    
    private func iphonePortraitButtons() -> some View {
        GeometryReader { proxy in
            let w = proxy.size.width
            let h = proxy.size.height
            ZStack {
                
                Button {
                    print("pause")
                } label: {
                    buttonIcon(systemName: "pause.rectangle.fill")
                        .frame(width: w * 0.16, height: h * 0.16)
                        .position(x: w * 0.158, y: h * 0.13)
                }
                
                Button {
                    print("info")
                } label: {
                    buttonIcon(systemName: "info.square.fill")
                        .frame(width: w * 0.16, height: h * 0.16)
                        .position(x: w * 0.675, y: h * 0.077)
                }
                
                Button {
                    print("clock")
                } label: {
                    buttonIcon(systemName: "clock.fill")
                        .frame(width: w * 0.16, height: h * 0.16)
                        .position(x: w * 0.310, y: h * 0.095)
                }
            }
        }
    }
    
    var body: some View {
        Image(systemName: "tortoise")
            .resizable()
            .scaledToFit()
            .foregroundStyle(.orange)
            .background(.blue.opacity(0.1))
            .overlay {
                iphonePortraitButtons()
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

Screenshot