[英]Styling the cancel button in a UISearchBar
我有一個 UISearchBar 有一個取消按鈕(它使用-(void)setShowsCancelButton:animated
)。 我已經像這樣更改了搜索欄的tintColor
以嘗試獲得灰色搜索欄:
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
searchBar.tintColor = [UIColor colorWithWhite:0.8 alpha:1.0];
這就是現在的樣子 - 注意取消按鈕也是灰色的: http : //twitpic.com/c0hte
有沒有辦法單獨設置取消按鈕的顏色,使其看起來更像這樣: http : //twitpic.com/c0i6q
您可以使用UIAppearance
設置取消按鈕的樣式,而無需迭代UISearchBar
子視圖,但UIButton
標頭當前沒有任何用UI_APPEARANCE_SELECTOR
注釋的方法。
編輯:向下鑽取子視圖,直到您獲得取消按鈕
但這通常返回 nil 直到searchBar.setShowsCancelButton(true, animated: true)
被調用。
extension UISearchBar {
var cancelButton : UIButton? {
if let view = self.subviews.first {
for subView in view.subviews {
if let cancelButton = subView as? UIButton {
return cancelButton
}
}
}
return nil
}
}
更改“取消”按鈕的標題:
[[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:@"newTitle" forState:UIControlStateNormal];
Swift 等效項:
let cancelButton = UIButton.appearance(whenContainedInInstancesOf: [UISearchBar.self])
cancelButton?.setTitle("cancel".localized, for: .normal)
在 iOS 5.0+ 中,您可以使用appearnce
代理。
在顯示搜索欄之前。:
UIBarButtonItem *searchBarButton = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
[searchBarButton setBackgroundImage:myCancelButtonImageNormal forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[searchBarButton setBackgroundImage:myCancelButtonImageHighlighted forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesNormal forState:UIControlStateNormal];
[searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesHighlighted forState:UIControlStateHighlighted];
如果你使用[UIButton appearanceWhenContainedIn:[UISearchBar class], nil]
,它會影響其他按鈕(例如清除按鈕)。 所以,你最好不要使用UIButton
的外觀。 試試UIBarButtonItem
。
盡管這可能與原始問題不完全相關,但該解決方案在嘗試自定義 UISearchBar 中的取消按鈕的更大意義上仍然適用。 認為這將幫助陷入這種情況的其他人。
我的情況是更改取消按鈕的標題,但有一點變化,其中我不想在默認情況下顯示取消按鈕,而只想在用戶進入搜索模式時(通過在搜索文本字段內單擊)顯示它)。 此時,我希望取消按鈕帶有標題“完成”(“取消”對我的屏幕賦予不同的含義,因此自定義)。
盡管如此,這就是我所做的(caelavel 和 Arenim 解決方案的組合):
使用以下兩種方法將 UISearchBar 子類化為 MyUISearchBar:
-(void) setCloseButtonTitle: (NSString *) title forState: (UIControlState)state
{
[self setTitle: title forState: state forView:self];
}
-(void) setTitle: (NSString *) title forState: (UIControlState)state forView: (UIView *)view
{
UIButton *cancelButton = nil;
for(UIView *subView in view.subviews){
if([subView isKindOfClass:UIButton.class])
{
cancelButton = (UIButton*)subView;
}
else
{
[self setTitle:title forState:state forView:subView];
}
}
if (cancelButton)
[cancelButton setTitle:title forState:state];
}
在使用此搜索欄的視圖控制器中,以下代碼負責顯示取消按鈕並自定義其標題:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
MyUISearchBar *sBar = (MyUISearchBar *)searchBar;
[sBar setShowsCancelButton:YES];
[sBar setCloseButtonTitle:@"Done" forState:UIControlStateNormal];
}
奇怪的是,當退出搜索模式時,我沒有做任何事情來隱藏取消按鈕,因為它默認是隱藏的。
你想做的事情很艱難。 沒有內置的鈎子可以到達取消按鈕。
但是,如果您願意打開引擎蓋,有幾種選擇。
首先,UISearchBar 是一個 UIView,而 Cancel 按鈕也是一個視圖,正如您所期望的那樣,它作為子視圖添加到搜索欄中。
我進行了一些實驗,可以告訴您,當按鈕在屏幕上時,它的大小為 48,30。
因此,在 viewWillAppear 中,您可以執行以下操作:
在 [searchBar subviews] 中查找大小為 48,30 的取消按鈕視圖。 (似乎只有一個——這可能會改變……)你可以加倍小心,尋找一個位置大致正確的(橫向和縱向不同)。
向取消按鈕添加子視圖。
子視圖應該是一個 UIControl(這樣你就可以設置 enabled = NO,以確保觸摸事件到達實際的取消按鈕)
它需要有正確的顏色和圓角; 由於我還不明白的原因,您需要捏造大小(55,30 似乎有效)
如果 searchBar.showsCancelButton 始終為 YES,這將起作用; 如果您希望它在不編輯搜索字符串時消失,則每次出現取消按鈕時都需要找到一個鈎子來添加疊加層。
如您所見,這是一些丑陋的修補。 睜大眼睛做這件事。
您可以通過循環搜索欄的子視圖並檢查類類型(而不是大小)來找到取消按鈕:
UIButton *cancelButton = nil;
for(UIView *subView in yourSearchBar.subviews){
if([subView isKindOfClass:UIButton.class]){
cancelButton = (UIButton*)subView;
}
}
然后更改色調顏色:
[cancelButton setTintColor:[UIColor colorWithRed:145.0/255.0 green:159.0/255.0 blue:179.0/255.0 alpha:1.0]];
如果你想在UISearchBar
上配置你的取消按鈕,你應該從你的UISearchBar
對象中獲取UIButton
對象。 下面的例子
UISearchBar *s_bar = [[UISearchBar alloc] initWithFrame:CGRectMake(50,20,300,30)];
s_bar.delegate = self;
s_bar.barStyle = UIBarStyleDefault;
s_bar.showsCancelButton = YES;
UIButton *cancelButton;
for (id button in s_bar.subviews)
{
if ([button isKindOfClass:[UIButton class]])
{
cancelButton=(UIButton*)button;
break;
}
}
我將詳細回答有關 UIAppearance 技術的問題。 首先,您需要了解取消按鈕是一個私有的 UINavigationButton:UIButton。 經過一些檢查,似乎 UINavigationButton 會響應那些 UIAppearance 選擇器:
// inherited from UINavigationButton
@selector(setTintColor:)
@selector(setBackgroundImage:forState:style:barMetrics:)
@selector(setBackgroundImage:forState:barMetrics:)
@selector(setTitleTextAttributes:forState:)
@selector(setBackgroundVerticalPositionAdjustment:forBarMetrics:)
@selector(setTitlePositionAdjustment:forBarMetrics:)
@selector(setBackButtonBackgroundImage:forState:barMetrics:)
@selector(setBackButtonTitlePositionAdjustment:forBarMetrics:)
@selector(setBackButtonBackgroundVerticalPositionAdjustment:forBarMetrics:)
// inherited from UIButton
@selector(setTitle:forState:)
巧合的是,這些選擇器與 UIBarButtonItem 的選擇器相匹配。 這意味着技巧是使用兩個單獨的 UIAppearance 來處理私有類 UINavigationButton。
/* dual appearance technique by Cœur to customize a UINavigationButton */
Class barClass = [UISearchBar self];
UIBarButtonItem<UIAppearance> *barButtonItemAppearanceInBar = [UIBarButtonItem appearanceWhenContainedIn:barClass, nil];
[barButtonItemAppearanceInBar setTintColor:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... style:... barMetrics:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... barMetrics:...];
[barButtonItemAppearanceInBar setTitleTextAttributes:... forState:...];
[barButtonItemAppearanceInBar setBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
[barButtonItemAppearanceInBar setTitlePositionAdjustment:... forBarMetrics:...];
// only for a backButton in an UINavigationBar, not for a cancelButton in an UISearchBar
//[barButtonItemAppearanceInBar setBackButtonBackgroundImage:... forState:... barMetrics:...];
//[barButtonItemAppearanceInBar setBackButtonTitlePositionAdjustment:... forBarMetrics:...];
//[barButtonItemAppearanceInBar setBackButtonBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
UIButton<UIAppearance> *buttonAppearanceInBar = [UIButton appearanceWhenContainedIn:barClass, nil];
// warning: doesn't work for iOS7+
[buttonAppearanceInBar setTitle:... forState:...];
這將使您可以根據需要自定義“取消”按鈕。
自定義 UISearchBar 和覆蓋方法 -addSubview:
- (void) addSubview:(UIView *)view {
[super addSubview:view];
if ([view isKindOfClass:UIButton.class]) {
UIButton *cancelButton = (UIButton *)view;
[cancelButton setBackgroundImage:[UIImage imageNamed:@"xxxx.png"] forState:UIControlStateNormal];
[cancelButton setBackgroundImage:[UIImage imageNamed:@"yyyy.png"] forState:UIControlStateHighlighted];
}
}
斯威夫特 2.1.1:
沒有簡單的方法來掛鈎和設置搜索欄的樣式,您需要從搜索欄中手動抓取子視圖,然后應用您的更改。
var cancelButton: UIButton
let topView: UIView = self.customSearchController.customSearchBar.subviews[0] as UIView
for subView in topView.subviews {
if subView.isKindOfClass(NSClassFromString("UINavigationButton")!) {
cancelButton = subView as! UIButton
cancelButton.enabled = true
cancelButton.setTitle("TestTitle", forState: UIControlState.Normal) // Change to set the title
cancelButton.setBackgroundImage(UIImage(named: "ImageName"), forState: .Normal) // Change this to set a custom cancel button image, set the title to "" to remove 'Cancel' text
}
}
首先,我要感謝來自這個https://stackoverflow.com/a/37381821/1473144 的@Eliott
我必須對他的回答進行一些調整才能在我下面的規范中工作。 請,我要求 OP 更新已接受的答案,因為它已經過時了。
Swift 3、iOS 10 和 Xcode 8.2.1
searchBar.showsCancelButton = true
var cancelButton: UIButton
let topView: UIView = self.searchBar.subviews[0] as UIView
for subView in topView.subviews {
if let pvtClass = NSClassFromString("UINavigationButton") {
if subView.isKind(of: pvtClass) {
cancelButton = subView as! UIButton
cancelButton.setTitle("", for: .normal)
cancelButton.tintColor = UIColor.black
cancelButton.setImage(#imageLiteral(resourceName: "searchX"), for: .normal)
}
}
}
初始化 UISearchBar 后,您可以探索它的子視圖並自定義每個子視圖。 例子:
for (UIView *view in searchBar.subviews) {
//if subview is the button
if ([[view.class description] isEqualToString:@"UINavigationButton"]) {
//change the button images and text for different states
[((UIButton *)view) setEnabled:YES];
[((UIButton *)view) setTitle:nil forState:UIControlStateNormal];
[((UIButton *)view) setImage:[UIImage imageNamed:@"button image"] forState:UIControlStateNormal];
[((UIButton *)view) setBackgroundImage:[UIImage imageNamed:@"button"] forState:UIControlStateNormal];
[((UIButton *)view) setBackgroundImage:[UIImage imageNamed:@"button_pressed"] forState:UIControlStateSelected];
[((UIButton *)view) setBackgroundImage:[UIImage imageNamed:@"button_pressed"] forState:UIControlStateHighlighted];
//if the subview is the background
}else if([[view.class description] isEqualToString:@"UISearchBarBackground"]) {
//put a custom gradient overtop the background
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[some uicolor] CGColor], (id)[[another uicolor] CGColor], nil];
[view.layer insertSublayer:gradient atIndex:0];
//if the subview is the textfield
}else if([[view.class description] isEqualToString:@"UISearchBarTextField"]){
//change the text field if you wish
}
}
對我來說很棒! 尤其是漸變:)
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar
{
NSArray *arr = [theSearchBar subviews];
UIButton *cancelButton = [arr objectAtIndex:3];
[cancelButton setTitle:@"yourtitle" forState:UIControlStateNormal];
}
只需記錄 arr 和查看索引控制所在的日志。 以同樣的方式你可以設置
UITextField
屬性:
NSArray *arr = [searchbar subviews];
UITextField *searchfield = [arr objectAtIndex:2];
[searchfield setTextAlignment:UITextAlignmentRight];
我的應用程序中有許多 UISearchBar 項目,所以我編寫了這個類別來添加一個屬性,以便您可以訪問mySearchBar.cancelButton
。 (如果您不熟悉類別,請在此處閱讀有關使用類別擴展對象的更多信息。)
請記住,您應該只在“取消”按鈕可見時訪問它,因為 UISearchBar 似乎每次顯示時都會創建一個新的按鈕對象。 不要保存指向 cancelButton 的指針,只在需要時獲取它:
@interface UISearchBar (cancelButton)
@property (readonly) UIButton* cancelButton;
- (UIButton *) cancelButton;
@end
@implementation UISearchBar (cancelButton)
- (UIButton *) cancelButton {
for (UIView *subView in self.subviews) {
//Find the button
if([subView isKindOfClass:[UIButton class]])
{
return (UIButton *)subView;
}
}
NSLog(@"Error: no cancel button found on %@", self);
return nil;
}
@end
愚蠢的方式
for(id cc in [SearchBar subviews])
{
if([cc isKindOfClass:[UIButton class]])
{
UIButton *btn = (UIButton *)cc;
......
Do whatever you want
.......
}
}
嗯,這里是函數,它可以改變取消的按鈕標簽。 如果需要,請修改它。 用法是:
nStaticReplaceStringInView(mySearchBar, @"Cancel", @"NewCancelButtonLabel");
void nStaticReplaceStringInView(UIView * view, NSString * haystack, NSString * needle)
{
for(int i=0; i<[view.subviews count]; i++)
{
nStaticReplaceStringInView([view.subviews objectAtIndex:i], haystack,needle);
}
if([view respondsToSelector:@selector(titleForState:)])
{
//NSLog(@"%@ || %@",[view titleForState:UIControlStateNormal], haystack);
if(NSStrEq([view titleForState:UIControlStateNormal] , haystack))
{
[view setTitle: needle forState: UIControlStateNormal];
}
}
}
extension UISearchBar {
var cancelButton : UIButton? {
let topView: UIView = self.subviews[0] as UIView
if let pvtClass = NSClassFromString("UINavigationButton") {
for v in topView.subviews {
if v.isKind(of: pvtClass) {
return v as? UIButton
}
}
}
return nil
}
}
UISearchBar *searchBar;
[searchBar setShowsCancelButton:YES animated:YES];
UIButton *cancelButton =
YES == [searchBar respondsToSelector:NSSelectorFromString(@"cancelButton")] ?
[searchBar valueForKeyPath:@"_cancelButton"] : nil;
cancelButton.titleEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 10);
[cancelButton setTitle:@"New :)" forState:UIControlStateNormal];
對於 iOS 11 和 Swift 4。創建 UISearchController 的子類。 覆蓋方法:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("layout")
if let btn = searchBar.subviews[0].subviews[2] as? UIButton {
btn.frame = CGRect(x: 306, y: 20, width: 53, height: 30)
}
}
對於 iOS 10 及更高版本,請使用以下方法
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTintColor:[UIColor blackColor]];
設置cancelButton
按鈕樣式的最佳方法是不使用UIAppearance
就像這樣,它適用於Swift5
iOS13
,它也適用於UISearchResultController.searchBar
extension UISearchBar {
func changeSearchBarAppearance(appearance: MyAppearance) {
self.barTintColor = appearance.searchbar.barTintColor
self.tintColor = appearance.searchbar.tintColor
if let textField = self.subviews.first?.subviews.last?.subviews.first {
textField.tintColor = .black
}
}
}
設置serachBar
tintColor
將設置tintColor
所有項目,包括的cancelButton
但此在的閃光燈searchField
也將使用相同的設置tintColor
所以找到textfield
,並設置其tintColor
將解決這個問題信號燈
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.