admin管理员组

文章数量:1203178

I have created a Sentence creator like in here(Pure SwiftUI). Button dragging and positioning is working fine. The problem is,

  • While dragging the button, it will not displaying on Sentence area & wise versa. (There are 2 areas, Sentence & Button area)
  • After dropping the button it's positioned without any issue.
  • Other behaviors are working as expected

Can anyone help me how to fix this issue?

struct SwiftUIDraggableButtonView: View {
    @State private var buttons: [DraggableButtonModel] = [
        DraggableButtonModel(id: 1, text: "me"),
        DraggableButtonModel(id: 2, text: "foreign friends."),
        DraggableButtonModel(id: 3, text: "to me,"),
        DraggableButtonModel(id: 4, text: "to make"),
        DraggableButtonModel(id: 5, text: "means")
    ]
    @State private var sentenceButtons: [DraggableButtonModel] = []

    var body: some View {
        VStack(spacing: 20) {
            // Unified FlowLayout for sentence and default areas
            FlowLayout(spacing: 5, items: sentenceButtons) { button in
                DraggableButtonNew(
                    model: button,
                    isInSentence: true
                ) { action in
                    handleButtonAction(action, button: button)
                }
                .transition(.move(edge: .bottom))
            }
            .padding()
            .frame(height: 200)  // Set height for sentence area
            .background(Color.gray)
            .zIndex(0)

            // Default button area
            FlowLayout(spacing: 10, items: buttons) { button in
                DraggableButtonNew(
                    model: button,
                    isInSentence: false
                ) { action in
                    handleButtonAction(action, button: button)
                }
                .allowsHitTesting(!button.isDisabled)  // Disable interaction for disabled buttons
                .transition(.move(edge: .top))
            }
            .frame(height: 200)  // Set height for default button area
            .zIndex(1)
            Spacer()
        }
        .padding()
    }

    private func handleButtonAction(_ action: DraggableButtonAction, button: DraggableButtonModel) {
        withAnimation {
            switch action {
            case .tap, .drag:
                // Handle button tap: move button between sentence and default areas
                if let index = sentenceButtons.firstIndex(where: { $0.id == button.id }) {
                    // Button is in the sentence area, move it back to default
                    sentenceButtons.remove(at: index)
                    if let defaultIndex = buttons.firstIndex(where: { $0.id == button.id }) {
                        buttons[defaultIndex].isDisabled = false // Re-enable in default area
                    }
                } else if let defaultIndex = buttons.firstIndex(where: { $0.id == button.id }),
                          !buttons[defaultIndex].isDisabled {
                    // Button is in default area, move it to sentence
                    buttons[defaultIndex].isDisabled = true
                    sentenceButtons.append(button)
                }
            }
        }
    }
}

// FlowLayout for wrapping buttons in multiple lines
struct FlowLayout<Data: RandomAccessCollection, Content: View>: View
where Data.Element: Identifiable {
    let spacing: CGFloat
    let items: Data
    let content: (Data.Element) -> Content

    var body: some View {
        var width: CGFloat = 0
        var height: CGFloat = 0

        return GeometryReader { geometry in
            ZStack(alignment: .topLeading) {
                ForEach(items) { item in
                    content(item)
                        .alignmentGuide(.leading) { d in
                            if abs(width - d.width) > geometry.size.width {
                                width = 0
                                height -= d.height + spacing
                            }
                            let result = width
                            if item.id == items.last?.id {
                                width = 0
                            } else {
                                width -= d.width + spacing
                            }
                            return result
                        }
                        .alignmentGuide(.top) { _ in
                            let result = height
                            if item.id == items.last?.id {
                                height = 0
                            }
                            return result
                        }
                }
            }
        }
        .frame(maxHeight: .infinity, alignment: .topLeading)
    }
}
// Draggable Button
struct DraggableButtonNew: View {
    let model: DraggableButtonModel
    let isInSentence: Bool
    let actionHandler: (DraggableButtonAction) -> Void
    @State private var offset: CGSize = .zero

    var body: some View {
        Text(model.text)
            .padding(8)
            .background(isInSentence ? Color.orange : model.isDisabled ? Color.gray : Color.orange)
            .foregroundColor(Color.white)
            .offset(offset)
            .zIndex(offset == .zero ? 0 : 1)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        offset = value.translation
                    }
                    .onEnded { _ in
                        actionHandler(.drag)
                        offset = .zero
                    }
            )
            .onTapGesture {
                actionHandler(.tap)
            }
    }
}

// Button Model
struct DraggableButtonModel: Identifiable, Equatable {
    let id: Int
    let text: String
    var isDisabled: Bool = false
}
enum DraggableButtonAction {
    case tap
    case drag
}

I have created a Sentence creator like in here(Pure SwiftUI). Button dragging and positioning is working fine. The problem is,

  • While dragging the button, it will not displaying on Sentence area & wise versa. (There are 2 areas, Sentence & Button area)
  • After dropping the button it's positioned without any issue.
  • Other behaviors are working as expected

Can anyone help me how to fix this issue?

struct SwiftUIDraggableButtonView: View {
    @State private var buttons: [DraggableButtonModel] = [
        DraggableButtonModel(id: 1, text: "me"),
        DraggableButtonModel(id: 2, text: "foreign friends."),
        DraggableButtonModel(id: 3, text: "to me,"),
        DraggableButtonModel(id: 4, text: "to make"),
        DraggableButtonModel(id: 5, text: "means")
    ]
    @State private var sentenceButtons: [DraggableButtonModel] = []

    var body: some View {
        VStack(spacing: 20) {
            // Unified FlowLayout for sentence and default areas
            FlowLayout(spacing: 5, items: sentenceButtons) { button in
                DraggableButtonNew(
                    model: button,
                    isInSentence: true
                ) { action in
                    handleButtonAction(action, button: button)
                }
                .transition(.move(edge: .bottom))
            }
            .padding()
            .frame(height: 200)  // Set height for sentence area
            .background(Color.gray)
            .zIndex(0)

            // Default button area
            FlowLayout(spacing: 10, items: buttons) { button in
                DraggableButtonNew(
                    model: button,
                    isInSentence: false
                ) { action in
                    handleButtonAction(action, button: button)
                }
                .allowsHitTesting(!button.isDisabled)  // Disable interaction for disabled buttons
                .transition(.move(edge: .top))
            }
            .frame(height: 200)  // Set height for default button area
            .zIndex(1)
            Spacer()
        }
        .padding()
    }

    private func handleButtonAction(_ action: DraggableButtonAction, button: DraggableButtonModel) {
        withAnimation {
            switch action {
            case .tap, .drag:
                // Handle button tap: move button between sentence and default areas
                if let index = sentenceButtons.firstIndex(where: { $0.id == button.id }) {
                    // Button is in the sentence area, move it back to default
                    sentenceButtons.remove(at: index)
                    if let defaultIndex = buttons.firstIndex(where: { $0.id == button.id }) {
                        buttons[defaultIndex].isDisabled = false // Re-enable in default area
                    }
                } else if let defaultIndex = buttons.firstIndex(where: { $0.id == button.id }),
                          !buttons[defaultIndex].isDisabled {
                    // Button is in default area, move it to sentence
                    buttons[defaultIndex].isDisabled = true
                    sentenceButtons.append(button)
                }
            }
        }
    }
}

// FlowLayout for wrapping buttons in multiple lines
struct FlowLayout<Data: RandomAccessCollection, Content: View>: View
where Data.Element: Identifiable {
    let spacing: CGFloat
    let items: Data
    let content: (Data.Element) -> Content

    var body: some View {
        var width: CGFloat = 0
        var height: CGFloat = 0

        return GeometryReader { geometry in
            ZStack(alignment: .topLeading) {
                ForEach(items) { item in
                    content(item)
                        .alignmentGuide(.leading) { d in
                            if abs(width - d.width) > geometry.size.width {
                                width = 0
                                height -= d.height + spacing
                            }
                            let result = width
                            if item.id == items.last?.id {
                                width = 0
                            } else {
                                width -= d.width + spacing
                            }
                            return result
                        }
                        .alignmentGuide(.top) { _ in
                            let result = height
                            if item.id == items.last?.id {
                                height = 0
                            }
                            return result
                        }
                }
            }
        }
        .frame(maxHeight: .infinity, alignment: .topLeading)
    }
}
// Draggable Button
struct DraggableButtonNew: View {
    let model: DraggableButtonModel
    let isInSentence: Bool
    let actionHandler: (DraggableButtonAction) -> Void
    @State private var offset: CGSize = .zero

    var body: some View {
        Text(model.text)
            .padding(8)
            .background(isInSentence ? Color.orange : model.isDisabled ? Color.gray : Color.orange)
            .foregroundColor(Color.white)
            .offset(offset)
            .zIndex(offset == .zero ? 0 : 1)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        offset = value.translation
                    }
                    .onEnded { _ in
                        actionHandler(.drag)
                        offset = .zero
                    }
            )
            .onTapGesture {
                actionHandler(.tap)
            }
    }
}

// Button Model
struct DraggableButtonModel: Identifiable, Equatable {
    let id: Int
    let text: String
    var isDisabled: Bool = false
}
enum DraggableButtonAction {
    case tap
    case drag
}
Share Improve this question edited Jan 21 at 7:26 mmk asked Jan 20 at 23:45 mmkmmk 1923 silver badges19 bronze badges 7
  • Please try creating a minimal example so that others can help & find your question better :) – Stoic Commented Jan 21 at 2:14
  • @Stoic, I developed this with minimal as you mentioned. Unfortunately minimal examples are fine without any issue. With more complexity, It has some issue.

    本文标签: iosButton dragging visible issue in swiftUIStack Overflow