How to make a header of a picker sticky in SwiftUI 2.0

Viewed 344

I want to add a searchbar to a picker in SwiftUI 2.0. The below demo code implements this, but the searchbar is part of the scrolling list instead of being sticky at the top when the user scrolls through the list. How can I change this?

import SwiftUI
import UIKit

struct ContentView: View {
    @State private var selection = 0
    @State private var searchText = ""

    var body: some View {
        NavigationView {
            Form {
                Picker(selection: $selection, label: Text("Picker")) {
                    SearchBar(text: $searchText, placeholder: "Search")
                    ForEach(1 ..< 21) { index in
                        Text(String(index)).tag(index)
                    }
                }
            }
        }
    }
}

struct SearchBar: UIViewRepresentable {
    @Binding var text: String
    var placeholder: String

    func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
        let searchBar = UISearchBar(frame: .zero)
        searchBar.delegate = context.coordinator

        searchBar.placeholder = placeholder
        searchBar.autocapitalizationType = .none
        searchBar.searchBarStyle = .minimal
        return searchBar
    }

    func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
        uiView.text = text
    }

    func makeCoordinator() -> SearchBar.Coordinator {
        return Coordinator(text: $text)
    }

    class Coordinator: NSObject, UISearchBarDelegate {
        @Binding var text: String

        init(text: Binding<String>) {
            _text = text
        }

        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            text = searchText
        }
    }
}
1 Answers

I could not make it work with Picker. But I have made the exact thing using below code. Hopefully, this will help you.

import SwiftUI

import UIKit

struct ContentView: View {
    @State private var selection = 0
    @State var navigateBack = false

var body: some View {
    NavigationView {
        Form {
            NavigationLink(
                destination: ItemSelectionView(selectedIndex: $selection, navigateBack: $navigateBack),
                isActive: $navigateBack,
                label: {
                    Text("Picker")
                })
                .overlay(
                    HStack {
                        Spacer()
                        Text(selection.description)
                    }
                    .padding(.trailing)
                )
        }
    }
    
    
    }
}
struct SearchBar: UIViewRepresentable {
    @Binding var text: String
    var placeholder: String

func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
    let searchBar = UISearchBar(frame: .zero)
    searchBar.delegate = context.coordinator
    
    searchBar.placeholder = placeholder
    searchBar.autocapitalizationType = .none
    searchBar.searchBarStyle = .minimal
    return searchBar
}

func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
    uiView.text = text
}

func makeCoordinator() -> SearchBar.Coordinator {
    return Coordinator(text: $text)
}

class Coordinator: NSObject, UISearchBarDelegate {
    @Binding var text: String
    
    init(text: Binding<String>) {
        _text = text
    }
    
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        text = searchText
    }
}
}

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
    }
}

struct ItemSelectionView: View {
    @Binding var selectedIndex: Int
    @Binding var navigateBack: Bool

let columns: [GridItem] = [
    GridItem(.flexible())
]
@State private var searchText = ""
var body: some View {
    ScrollView {
        LazyVGrid(columns: columns, alignment: .leading, pinnedViews: [.sectionHeaders ]) {
            Section(header:  SearchBar(text: $searchText, placeholder: "Search")) {
                ForEach(1 ..< 21) { index in
                    HStack {
                        Text(String(index))
                        Spacer()
                        if selectedIndex == index
                        {
                            Image(systemName: "checkmark")
                        }
                    }
                    .padding(.horizontal)
                    .contentShape(Rectangle())
                    .onTapGesture {
                        selectedIndex = index
                        navigateBack.toggle()
                    }
                    
                    Divider()
                }
            }
        }
    }
    }
}
Related