Search code examples
swiftbuttonswiftui

How to align the an image on top of a button in swiftui?


I wish to add a 'trash' image on the top-right side of each button when 'Delete Button' is pressed, so that when user hits the trash image, the button will be removed from the vstack. I think I should use zstack to position the trash image but I don't know how for now. Below shows where the trash image should be located in each button.

enter image description here

Also, when I press the 'Delete Button', it seems that each button's text size and spacing with another button is changed slightly. How do I overcome this problem? The button position, spacing, textsize should be unchanged when 'Delete Button' is hit.

struct someButton: View {
    @Environment(\.editMode) var mode
    @ObservedObject var someData = SomeData()
    @State var newButtonTitle = ""
    @State var isEdit = false

    var body: some View {
        NavigationView{
//            List{ // VStack
                VStack{
                    VStack{
                        ForEach(Array(someData.buttonTitles.keys.enumerated()), id: \.element){ ind, buttonKeyName in
//

                               Button(action: {
                                self.someData.buttonTitles[buttonKeyName] = !self.someData.buttonTitles[buttonKeyName]!
                                print("Button pressed! buttonKeyName is: \(buttonKeyName) Index is \(ind)")
                                print("bool is \(self.someData.buttonTitles[buttonKeyName]!)")



                               }) {
                                   HStack{ //HStack, ZStack
                                    if self.isEdit{
                                           Image(systemName: "trash")
                                            .foregroundColor(.red)
                                            .onTapGesture{
                                                print("buttonkey \(buttonKeyName) will be deleted")
                                                self.deleteItem(ind: ind)
                                            }
                                    }


                                    Text(buttonKeyName)
//                                           .fontWeight(.semibold)
//                                           .font(.title)
                                   }



                               }
                               .buttonStyle(GradientBackgroundStyle(isTapped: self.someData.buttonTitles[buttonKeyName]!))
                               .padding(.bottom, 20)



                        }
                    }


                    HStack{
                        TextField("Enter new button name", text: $newButtonTitle){
                            self.someData.buttonTitles[self.newButtonTitle] = false
                            self.newButtonTitle = ""
                        }
                    }


                }
                .navigationBarItems(leading: Button(action: {self.isEdit.toggle()}){Text("Delete Button")},
                                    trailing: EditButton())

//                .navigationBarItems(leading: Button(action: {}){Text("ergheh")})
//            }

        }

    }


    func deleteItem(ind: Int) {
        let key = Array(someData.buttonTitles.keys)[ind]
        print(" deleting ind \(ind), key: \(key)")
       self.someData.buttonTitles.removeValue(forKey: key)
       }


}




struct GradientBackgroundStyle: ButtonStyle {
    var isTapped: Bool

    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .frame(maxWidth: .infinity, maxHeight: 50)
            .padding()
            .foregroundColor(isTapped ? Color.blue : Color.black)
            .background(LinearGradient(gradient: Gradient(colors: [Color("DarkGreen"), Color("LightGreen")]), startPoint: .leading, endPoint: .trailing))

            .cornerRadius(40)
            .overlay(RoundedRectangle(cornerRadius: 40)
                .stroke(isTapped ? Color.blue : Color.black, lineWidth: 4))
            .shadow(radius: 40)
            .padding(.horizontal, 20)
            .scaleEffect(configuration.isPressed ? 0.9 : 1.0)
//


    }
}



class SomeData: ObservableObject{
    @Published var buttonTitles: [String: Bool] = ["tag1": false, "tag2": false]
}

Solution

  • Here is a demo of possible approach. Tested with Xcode 11.4 / iOS 13.4 (with some replicated code)

    demo

    var body: some View {
           Button(action: { }) {
            Text("Name")
           }
           .buttonStyle(GradientBackgroundStyle(isTapped: tapped))
           .overlay(Group {
                if self.isEdit {
                    ZStack {
                        Button(action: {print(">> Trash Tapped")}) {
                           Image(systemName: "trash")
                                .foregroundColor(.red).font(.title)
                        }.padding(.trailing, 40)
                        .alignmentGuide(.top) { $0[.bottom] }
                    }.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
    
                }
           })
           .padding(.bottom, 20)
    }