SwiftUI - кнопка внутри представления коллекции не может быть задействована полностью

У меня есть набор кнопок, которые нужно показать пользователю, и я использовал CollectionView для выравнивания кнопок. Каждая кнопка представляет собой Vstack с компонентами Image и Text. Касание реагирует только на изображение, но не на текст и пространство вокруг него.

Я пытаюсь решить эту проблему, чтобы она реагировала по всей кнопке. Я нашел предложения

  • чтобы установить ContentShape в прямоугольник, и это не сработало
  • используйте Hstack для вставки пробелов слева и справа от текста, но это тоже не сработало.

Пример кода: ToolBarItem:

      var body: some View {
    VStack {
        
        Button(action: {
            
            // Delegate event to caller/parent view
            self.onClickAction(self.toolBarItem)
            
            }) {
            
            VStack {
                HStack {
                    Spacer()
                    Image(self.toolBarItem.selectedBackgroundImage)
                    .renderingMode(.original)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .padding(EdgeInsets(top: 5, leading: 3, bottom: 0, trailing: 3))
                    .frame(width: CGFloat(self.toolBarItem.cellWidth * 0.60),
                           height: CGFloat(self.toolBarItem.cellHeight * 0.60))
                    Spacer()
                }
               .contentShape(Rectangle())
               
          
                HStack {
                  Spacer()
                  Text(self.toolBarMenuInfo.specialSelectedName)
                    .foregroundColor(Color.red)
                    .padding(EdgeInsets(top: 0, leading: 0, bottom: 5, trailing: 0))
                  Spacer()
                }
                 .contentShape(Rectangle())
            }
            .frame(width: CGFloat(self.toolBarItem.cellWidth),
                               height: CGFloat(self.toolBarItem.cellHeight))
            .background(Color.blue.opacity(0.5))
            }
    }
}

Вышеупомянутый ToolBarItem помещается в представление коллекции (созданный мной настраиваемый объект) для необходимого количества элементов. Обратитесь к вложению, и нажатие происходит только на изображении, обведенном зеленой маркировкой.

у кого-нибудь была подобная проблема? Любые входы приветствуются.

2 ответа

Вы пробовали поставить .contentShape(Rectangle())в целом VStackвнутри Buttonили на самой кнопке? Вероятно, это должно решить эту проблему.

Я сильно подозреваю, что ваш вопрос связан с границей. но я точно не знаю, потому что вы не предоставили этот код.

Вот версия представления кнопки, которая даст вам желаемый эффект.

      struct FloatingToolbarButtonView: View {
    @Binding var model: ButtonModel
    let size: CGSize
    var body: some View {
        Button(action: {
            //Set the model's variable to selected
            model.isSelected.toggle()
            //Perform the action
            model.onClick()
        }, label: {
            VStack {
                //REMOVE systemName: in your code
                Image(systemName: model.imageName)
                //.renderingMode(.original)
                    .resizable()
                //Maintains proportions
                    .scaledToFit()
                //Set Image color
                    .foregroundColor(.white)
                //Works with most images to change color
                    .colorMultiply(model.colorSettings.imageNormal)
                    .padding(5)
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                //Set border color/width
                    .border(Color.green, width: model.isSelected ? 3:0)
                Spacer()
                
                Text(model.label)
                //Set Text color
                    .foregroundColor(model.colorSettings.labelNormal)
            }
            
            .padding(EdgeInsets(top: 5, leading: 3, bottom: 0, trailing: 3))
            
            
        }).frame(width: size.width, height: size.height, alignment: .center)
            .background(model.colorSettings.backgroundNormal)
    }
}

А вот так выглядит модель, которую я использовал

      //Holds Button information
struct ButtonModel: Identifiable{
    let id: UUID = UUID()
    var label: String
    var imageName: String
    ///Action to be called when the button is pressed
    var onClick: () -> Void
    ///identify if the user has selected this button
    var isSelected: Bool = false
    var colorSettings: ButtonColorSettings
}

Я создал кнопки в модели представления, поэтому мне легко установить actionи доступ isSelectedпо мере необходимости.

      //ViewModel that deals with all the button creation and onClick actions
class FloatingToolbarParentViewModel: ObservableObject{
    //Settings the buttons at this level lets you read `isPressed`
    @Published var horizontalButtons: [ButtonModel] = []
    @Published var moreButton: [ButtonModel] = []
    @Published var verticalButtons: [ButtonModel] = []
    
    init(){
         horizontalButtons = horizontalSamples
         moreButton = [mer]
         verticalButtons = veticalSamples
    }
}
//MARK: Buttons
extension FloatingToolbarParentViewModel{
    //MARK: SAMPLES fill in with your data
    var identify:ButtonModel {ButtonModel(label: "Identify", imageName: "arrow.up.backward", onClick: {print(#function + " Identfy")}, colorSettings: .white)}
    var tiltak:ButtonModel  {ButtonModel(label: "Tiltak", imageName: "scissors", onClick: {print(#function + " Tiltak")}, colorSettings: .white)}
    var tegn:ButtonModel { ButtonModel(label: "Tegn", imageName: "pencil", onClick: {print(#function + " Tegn")}, colorSettings: .white)}
    var bestand:ButtonModel  {ButtonModel(label: "Bestand", imageName: "leaf", onClick: {print(#function + " Identfy")}, colorSettings: .red)}
    var mer:ButtonModel  {ButtonModel(label: "Mer", imageName: "ellipsis.circle", onClick: {print(#function + " Mer")}, colorSettings: .red)}
    var kart:ButtonModel  {ButtonModel(label: "Kart", imageName: "map.fill", onClick: {print(#function + " Kart")}, colorSettings: .white)}
    var posisjon:ButtonModel  {ButtonModel(label: "Posisjon", imageName: "magnifyingglass", onClick: {print(#function + " Posisjon")}, colorSettings: .white)}
    var spor:ButtonModel  {ButtonModel(label: "Spor", imageName: "circle.fill", onClick: {print(#function + " Spor")}, colorSettings: .red)}
    
    var horizontalSamples :[ButtonModel]  {[identify,tiltak,tegn,bestand]}
    var veticalSamples :[ButtonModel]  {[kart,posisjon,spor]}
}

Остальной код для запуска примера приведен ниже. Это на самом деле не нужно, но это даст вам рабочий продукт

      struct FloatingToolbarParentView: View {
    @State var region: MKCoordinateRegion = .init()
    @StateObject var vm: FloatingToolbarParentViewModel = .init()
    
    var body: some View {
        ZStack{
            Map(coordinateRegion: $region)
            ToolbarOverlayView( horizontalButtons: $vm.horizontalButtons, cornerButton: $vm.moreButton, verticalButtons: $vm.verticalButtons)
        }
    }
}
struct ToolbarOverlayView: View{
    @State var buttonSize: CGSize = .zero
    @Binding var horizontalButtons: [ButtonModel]
    @Binding var cornerButton: [ButtonModel]
    @Binding var verticalButtons: [ButtonModel]
    var body: some View{
        GeometryReader{ geo in
            VStack{
                HStack{
                    Spacer()
                    VStack{
                        Spacer()
                        FloatingToolbarView(buttons: $verticalButtons, buttonSize: buttonSize, direction: .vertical)
                    }
                }
                Spacer()
                HStack{
                    Spacer()
                    FloatingToolbarView(buttons: $horizontalButtons, buttonSize: buttonSize)
                    
                    FloatingToolbarView(buttons: $cornerButton, buttonSize: buttonSize)
                }
                //Adjust the button size on appear and when the orientation changes
                    .onAppear(perform: {
                        setButtonSize(size: geo.size)
                    })
                    .onChange(of: geo.size.width, perform: { new in
                        setButtonSize(size: geo.size)
                    })
            }
        }
    }
    //Sets the button size using and minimum and maximum values accordingly
    //landscape and portrait have oppositive min and max
    func setButtonSize(size: CGSize){
        buttonSize = CGSize(width: min(size.width, size.height) * 0.15, height: max(size.width, size.height)  * 0.1)
    }
}


//Toolbar group for an array of butons
struct FloatingToolbarView: View {
    @Binding var buttons :[ButtonModel]
    let buttonSize: CGSize
    var direction: Direction = .horizontal
    var body: some View {
        Group{
            switch direction {
            case .horizontal:
                HStack(spacing: 0){
                    ForEach($buttons){$button in
                        FloatingToolbarButtonView(model: $button, size: buttonSize)
                    }
                }
            case .vertical:
                VStack(spacing: 0){
                    ForEach($buttons){$button in
                        FloatingToolbarButtonView(model: $button, size: buttonSize)
                    }
                }
            }
        }
    }
    enum Direction{
        case horizontal
        case vertical
    }
}


@available(iOS 15.0, *)
struct FloatingToolbarParentView_Previews: PreviewProvider {
    static var previews: some View {
        FloatingToolbarParentView()
        FloatingToolbarParentView().previewInterfaceOrientation(.landscapeLeft)
    }
}
//Holds Button Color information
//You havent provided much info on this so I assume that you are setting the colors somewhere
struct ButtonColorSettings{
    var labelNormal: Color
    var imageNormal: Color
    var backgroundNormal: Color
    //Sample Color configuration per image
    static var white = ButtonColorSettings(labelNormal: .white, imageNormal: .white, backgroundNormal: .black.opacity(0.5))
    static var red = ButtonColorSettings(labelNormal: .black, imageNormal: .red, backgroundNormal: .white)
}
Другие вопросы по тегам