在SwiftUI的TextField中实现令牌

5

如何使SwiftUI中的TextField具有类似UISearchBar的token?

我尝试插入UISearchBar以便可以使用它们,但我失去了TextField和List之间交互的行为。

谢谢


暂时不行。您可以向Apple提供反馈,也许在下一版中会解决... - Asperi
你有想到任何解决方案吗?我想在我的文本字段中使用标记。 - mikro098
暂时无法直接使用令牌。要使用令牌,我们需要使用UITextField。 - João Colaço
2个回答

4

iOS 16将支持在SwiftUI的searchable视图修饰符中添加此功能,例如searchable(text:tokens:placement:prompt:token:)API文档

来自文档的示例

enum FruitToken: Identifiable, Hashable, CaseIterable {
    case apple
    case pear
    case banana
    var id: Self { self }
}

@State private var tokens: [FruitToken] = []

ProductList()
    .searchable(text: $text, tokens: $tokens) { token in
        switch token {
        case .apple: Text("Apple")
        case .pear: Text("Pear")
        case .banana: Text("Banana")
        }
    }

你能否提供一个例子,以便更加完整? - João Colaço
好主意,已更新! - CMash
能否使其在之前的iOS版本上运行? - Alec von Barnekow
很抱歉,这个功能只能在 iOS 16 及以上版本中使用。 - CMash
@CMash,令牌和“searchSuggestions”是解决同一问题的两种方式吗?还是完全不同的东西?是否可以同时使用搜索令牌和搜索建议? - user1046037

4

对于iOS 16以前的版本,最佳方法似乎是使用UIViewRepresentable结构来创建具有令牌的UISearchBar

它有一个用于搜索文本的绑定存储在数组中,searchText。在其中,每个元素将具有每个令牌的文本。

/// Brings UISearchBar to SwiftUI. This enables using tokens for searching for iOS and macOS.
struct TokenSearchBar {
    @Binding var searchText: [String]
}

extension TokenSearchBar: UIViewRepresentable {

    internal func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UISearchBar {
        //Configures the Search Bar
        let searchBar = UISearchBar()

        searchBar.delegate = context.coordinator

        return searchBar
    }

    func updateUIView(_ searchBar: UISearchBar, context: Context) {

        if context.coordinator.searchText != searchText {
            context.coordinator.searchText = searchText
        }
    }


    /// Coordinator and delegate for the searchbar
    internal class Coordinator: NSObject, UISearchBarDelegate {
        var parent: TokenSearchBar

        var searchBar: UISearchBar?

        var searchText: [String] = [] {
            didSet {
                guard let searchBar = searchBar else {
                    return
                }

                //get the current search array
                var tokenSearchText = searchText

                let lastSearchTextItem: String

                if tokenSearchText.isEmpty {
                    lastSearchTextItem = ""
                } else {
                    lastSearchTextItem = tokenSearchText.removeLast()
                }

                //compare them with the search array
                tokenSearchText.enumerated().forEach {
                    offset, searchString in

                    //Get the current tokens.
                    let currentTokens = searchBar.searchTextField.tokens

                    //Check if the number of tokens on display can be displayed with this offset. If not it will create a new token at the end.
                    if currentTokens.count > offset {

                        //Check if the token at this offset has the same object as the searchstring. If not will insert a new token on this offset.
                        //I will assume that all represented objects are strings
                        if let representedObject = currentTokens[offset].representedObject as? String, representedObject != searchString {
                            let newToken = UISearchToken(icon: nil, text: searchString)
                            newToken.representedObject = searchString

                            searchBar.searchTextField.tokens.insert(newToken, at: offset)
                        }

                    } else {
                        let newToken = UISearchToken(icon: nil, text: searchString)
                        newToken.representedObject = searchString

                        self.searchBar?.searchTextField.tokens.append(newToken)
                    }
                }

                //Trim the number of tokens to be equal to the tokenSearchText
                let tokensToRemove = searchBar.searchTextField.tokens.count - tokenSearchText.count
                if tokensToRemove > 0 {
                    searchBar.searchTextField.tokens.removeLast(tokensToRemove)
                }

                //make the search field text equal to lastSearchTextItem
                if searchBar.text != lastSearchTextItem {
                    searchBar.text = lastSearchTextItem
                }

                if parent.searchText != self.searchText {
                    parent.searchText = self.searchText

                }
            }
        }


        init(_ searchBar: TokenSearchBar) {
            self.parent = searchBar
        }

        //MARK: UISearchBarDelegate implementation
        func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
            self.searchBar = searchBar

            searchText = []
        }

        func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
            self.searchBar = searchBar

            //add a new level to the search
            if searchText.last?.trimmingCharacters(in: .whitespacesAndNewlines) != "" {
                searchText.append("")
            }
        }

        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            self.searchBar = searchBar

            if self.searchText.last != nil,  self.searchText[self.searchText.count - 1] != searchText {
                self.searchText[self.searchText.count - 1] = searchText
            } else {

                var tokenText = searchBar.searchTextField.tokens.compactMap{$0.representedObject as? String}

                tokenText.append(searchText)

                self.searchText = tokenText

            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接