iOS 13 - 自定义带有UISearchBar的搜索栏中_searchField无法工作

45

Xcode-11-Beta (ios13)之前,使用以下代码为自定义搜索栏设置value for key值可以正常工作。现在会出现以下崩溃日志。

'NSGenericException',reason:'禁止访问UISearchBar的_searchField实例变量。这是应用程序错误'

- (UITextField *)textField
{
 return [self valueForKey:@"_searchField"];
}

需要任何帮助。


1
不要使用valueForKey:去挖掘标准组件的私有子视图结构。这从来就不是有效的方法,而且它总是最终会出问题。使用提供的公共API完成你需要的功能。 - rmaddy
2
@rmaddy 感谢您宝贵的反馈。但是,我已经自定义了 UISearchBar。那么,现在有什么解决方案呢? - Pratik Sodha
据我所了解,我们没有任何一个解决方案? - Genevios
@PratikSodha - 请查看此链接UISearchBarSearchField BackgroundView color - Anbu.Karthik
12个回答

74
SDK现在提供了UISearchBar.searchTextField,因此您可以通过公共API简单地替换私有API实现。
searchBar.searchTextField.backgroundColor = [UIColor blueColor];

但现在这段代码在之前的Xcode上无法编译... 有什么建议吗? - tomeron11
如果您需要支持iOS 12及以下版本,但是您正在使用Xcode 11进行编译呢? - trusk
14
如果你需要支持早期版本的iOS,请用if #available(iOS 13.0, *) {}将其包装起来。在为iOS 13构建时,您将无法使用旧版本的Xcode。 - Jordan H
1
@JordanH,你的答案不错,但我需要支持xCode10,有人能帮助我吗? - guru

24

如果我们想在使用Xcode 11编译时同时支持iOS 12及更早版本,那么我们可以创建一个UISearchBar的扩展,从中获取文本字段。

extension UISearchBar {

    var textField : UITextField? {
        if #available(iOS 13.0, *) {
            return self.searchTextField
        } else { // Fallback on earlier versions
            for subview in subviews.first?.subviews ?? [] {
                if let textField = subview as? UITextField {
                    return textField
                }
            }
        }
        return nil
    }
}

使用方法

searchBar.textField?.font = UIFont.systemFont(ofSize: 15.0)

UISearchBar中,我们可以访问所有的textField属性并根据需要进行修改。

视图层次结构发生了变化。因此,通过打印(self.subviews[0]).subviews,其中selfUISearchBar


对于iOS 12及更早版本

enter image description here

对于iOS 13+

enter image description here

除了UISearchBarBackground之外,现在我们还有UISearchBarSearchContainerViewUISearchBarScopeContainerView,而UISearchBarTextField已经不存在了,被苹果提供的UISearchTextField类中的扩展所代替。

extension UISearchBar {

    
    open var searchTextField: UISearchTextField { get }
}

在iOS 13及以上的设备中,我们可以直接访问searchBar.searchTextField,但是在iOS 12及以下的设备中访问该属性将导致应用程序崩溃。

崩溃信息如下:

[UISearchBar searchTextField]: unrecognized selector sent to instance

enter image description here

这是苹果官方文档


4
当iOS部署目标版本低于13.0时,为什么Xcode编译器不会发出警告?通常在使用不适用于部署目标版本的API时会发生这种情况,这不是预编译器的重要工作吗? - Manuel
是的,UISearchTextField被标记了(例如,输入“UISearchTextField * textfield”会给我一个警告),但不幸的是,在SDK中,“searchBar.searchTextField”属性没有被标记为仅适用于iOS13。 - mackworth
在iOS 12上调用becomeFirstResponder时崩溃。 - BSK-Team
1
上次我检查(在Xcode 11.6上),这个属性终于被标记为iOS 13+。早该这样了... - gcharita

22

我也遇到了同样的崩溃问题,在IOS 13中,您不再需要使用.value(forKey:)方法。

这是导致崩溃的那一行代码:

if let searchField = searchController.searchBar.value(forKey: "_searchField") as? UITextField {

这就是解决方法:

let searchField = searchController.searchBar.searchTextField

1
你是如何找到导致崩溃的那行代码的? - Marlhex
它能够工作,但我的问题是之前用于键方法的早期值通常可以工作,现在在xcode 11.2.1上它是什么导致它崩溃? - Abhishek Maurya
这帮助了我,经历了数天的噩梦。 - faris97

9

请执行以下操作:

 var searchTextField: UITextField?
    if #available(iOS 13.0, *) {
        searchTextField = searchBar.searchTextField
    } else {
        if let searchField = searchBar.value(forKey: "searchField") as? UITextField {
            searchTextField = searchField
        }
    }

这将根据版本检查适当地为您提供搜索栏的文本字段。

7

在 Swift 5 中,使用键值 "searchField" 而非带下划线的键值仍然可以正常工作。

guard let searchField = searchBar.value(forKey: "searchField") as? UITextField else { return }

如果您仍在使用Xcode 10,那么必须按照这种方式进行操作,因为SDK 10 UISearchBar中没有“searchTextField”。但是,在iOS 13下运行时,它将起作用。 if #available(iOS 13.0, *) { return value(forKey: "searchField") as? UITextField } else { return value(forKey: "_searchField") as? UITextField } - GilroyKilroy
这是一个私有API吗?苹果会拒绝将应用程序提交到应用商店吗? - HamasN

4
如果您想检查iOS版本,请使用以下方法!
 if #available(iOS 13.0, *) {
    searchBar.searchTextField.clearButtonMode = .never
 } else {
    searchBar.textField.clearButtonMode = .never
 }

不要忘记文件扩展名:

extension UISearchBar {

 var textField : UITextField{
    return self.value(forKey: "_searchField") as! UITextField
 }
}

夏令时节省的答案!提示:对我而言,没有下划线也可以正常工作(self.value(forKey: "searchField") as! UITextField)。 - chocolate cake

3

在尝试访问属性时,将所有的“_”下划线删除后,它适用于所有 iOS 版本。


2
extension UISearchBar {

    var textField : UITextField? {
        if #available(iOS 13.0, *) {
            return self.searchTextField
        } else {
            // Fallback on earlier versions
            return value(forKey: "_searchField") as? UITextField
        }
        return nil
    }
}

0

我在取消按钮value forkey上遇到了崩溃。在iOS 13中是否可以自定义取消按钮?enter image description here


你是如何访问取消按钮的? - mohsin
让cancelButton = searchBar.value(forKey: "_cancelButton") as? UIButton @mohsin - Jiffin
1
通过在键名前删除下划线来解决问题。 let cancelButton = searchBar.value(forKey: "cancelButton") as? UIButton - Jiffin
是的,在您尝试访问的任何地方都要删除“_”。这对于几乎所有版本都有效。 - mohsin

0

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