IOS customize UISearchBar navigation bar to synchronize iOS11

Time:2020-11-23

The original UISearchBar of the system has undergone a change in IOS 11. The height of the original UISearchBar has changed from 44 to 56 (the estimation of the default height has been corrupted), and the style has also undergone some minor changes, such as the change of rounded corners in the state of no input, and the text of magnifying glass icon and text is no longer centered, but left. See the picture for details

Some mainstream apps also often embed searchBar in the navigation bar. Take Netease cloud music and Zhihu as examples. The home page is on the left and the search page is on the right (pay attention to the cursor).

Realization ideas and cases

The core idea is to set the title view of the navigation bar and the bar button item on the left and right. There are three main ways

  1. The title view of the navigation bar on the home page is implemented with button, and the search page uses searchBar.
  2. Both the title view of the home page and the navigation bar of the search page use the searchBar style to make different modifications to the two pages. In this way, we can reuse our customized searchBar to reduce redundancy.
  3. The title view of the home page navigation bar is implemented by button, and the search page is implemented by textfield. This approach is more thorough, more flexible, and relatively more complex.

Why does the title view above say “button” and not others? Others, of course, can be achieved. Button comes with ImageView and titlelabel. It’s easier to achieve what we want by setting the offset. Moreover, the view level is less and the fluency is more guaranteed.

case

For Netease cloud music home page and search page navigation bar layer level, titleview is implemented using mcsearchbar, and buttons on the left and right sides of the navigation bar are set. This is similar to the second idea mentioned above.

 

In the figure, you can clearly see that the navigation bar of Zhihu home page is composed of two buttons, and the search page uses textfield, which is similar to the third idea mentioned above.

actual combat

A navigation bar of the following style can be implemented by customizing the searchBar

First, customize an initialization method of UISearchBar, and observe the similarities and differences between the home page and the search page. For example, the size and background color of searchfield are consistent, which can be given directly, but the placeholder is different, so it should be provided when calling. By analogy, create a new ohsearchbar class and an initialization method

- (instancetype)initWithFrame:(CGRect)frame placeholder:(NSString *)placeholder textFieldLeftView:(UIImageView *)leftView showCancelButton:(BOOL)showCancelButton tintColor:(UIColor *)tintColor { 
 if (self = [super initWithFrame:frame]) {
  self.frame = frame;
  self.tintColor  =Tintcolor; // cursor color
  self.barTintColor = [UIColor whiteColor];
  self.placeholder = placeholder;
  self.showsCancelButton = showCancelButton;
  self.leftView  =Leftview; // used to replace the magnifying glass on the left
  [self setImage:[UIImage imageNamed:@"clear"] forSear chBarIcon:UISearchBarIconClear state : uicontrolstatenormal]; // replace the clearicon on the right during input
 }
 return self;
}

Create a home page ohomeviewcontroller, and set the titleview and rightbarbutton of the navigation bar

// navigation buttom
 UIButton *messageButton = [UIButton buttonWithType:UIButtonTypeSystem];
 [messageButton setImage:[UIImage imageNamed:@"msg"] forState:UIControlStateNormal];
 messageButton.bounds = CGRectMake(0, 0, 30, 30);
 UIBarButtonItem *messageBarButton = [[UIBarButtonItem alloc] initWithCustomView:messageButton];
 self.navigationItem.rightBarButtonItem = messageBarButton;
 
 // search bar
 UIImageView *leftView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"scan"]];
 leftView.bounds = CGRectMake(0, 0, 24, 24);
 self.ohSearchBar = [[OHSearchBar alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 44)
            Placeholder: @ "click me to jump"
          textFieldLeftView:leftView
           showCancelButton:NO
            tintColor:[UIColor clearColor]];
 self.navigationItem.titleView = self.ohSearchBar;

Let’s take a look at the effect, IOS 9 on the left and IOS 11 on the right

There are several differences

  1. Height of searchBar
  2. Magnifier and text position of textfield of searchBar
  3. The fillet of textfield is inconsistent
  4. If you are more careful, you will find that the textfield is not in the same position

Solution: the first and second questions are to determine whether the device is IOS 11. If so, set the height of the device, and if not, set the holder to the left. The key codes are as follows


if ([[UIDevice currentDevice] systemVersion].doubleValue >= 11.0) {
  [[self.heightAnchor constraintEqualToConstant:44.0] setActive:YES];
 } else {
  [self setLeftPlaceholder];
 }
- (void)setLeftPlaceholder {
 SEL centerSelector = NSSelectorFromString([NSString stringWithFormat:@"%@%@", @"setCenter", @"Placeholder:"]);
 if ([self respondsToSelector:centerSelector]) {
  BOOL centeredPlaceholder = NO;
  NSMethodSignature *signature = [[UISearchBar class] instanceMethodSignatureForSelector:centerSelector];
  NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
  [invocation setTarget:self];
  [invocation setSelector:centerSelector];
  [invocation setArgument:&centeredPlaceholder atIndex:2];
  [invocation invoke];
 }
}

For the third and fourth questions, the textfield is obtained by KVC and customized. Make the textfield position, size and fillet consistent.

- (void)layoutSubviews{
 [super layoutSubviews];
 // search field
 UITextField *searchField = [self valueForKey:@"searchField"];
 searchField.backgroundColor = DARK_BLUE_COLOR;
 searchField.textColor = [UIColor whiteColor];
 searchField.font = [UIFont systemFontOfSize:16];
 searchField.leftView = self.leftView;
 searchField.frame = CGRectMake(0, 8, SCREEN_WIDTH, 28);
 searchField.layer.cornerRadius = 5;
 searchField.layer.masksToBounds = YES;
 [searchField setValue:[UIColor whiteColor] forKeyPath:@"placeholderLabel.textColor"];
 [self setValue:searchField forKey:@"searchField"]; 
 self.searchTextPositionAdjustment  =(uioffset) {10, 0}; // cursor offset
}

Similarly, let’s look at the operation effect first

Originally thought that this is no problem, the result is simply pit

Textfild’s length, position and fillet are different. Explain the problem here

If you look at the searchBar at the top of the picture above, you will find that the textfield has a rounded corner on the left and a right angle on the right, indicating that it is intercepted. The scope of the navigation bar titleview is divided into that part, and the searchBar at the bottom can’t even miss the rightbarbutton, so it directly occupies the position. It is speculated that this is due to changes in the view level of IOS 11 navigation bar. You can learn about it here www.jianshu.com/p/352f101d6 … Or do it on their own, not in detail. Therefore, you should be careful about the size setting of the searchBar, and try to control it in an appropriate range.

The fillet of textfield is inconsistent. When you customize the fillet size, cancel its own fillet style


searchField.borderStyle = UITextBorderStyleNone;

If you view the view level, you will find that if you set titleview below IOS 11, the default coordinate of X is 12, while IOS 11 is 0. Therefore, if the X coordinate of textfield is set, it must be 12 more in IOS 11 to be consistent.

 

Modify the code above the code

- (void)layoutSubviews{
 [super layoutSubviews];
 // search field
 UITextField *searchField = [self valueForKey:@"searchField"];
 searchField.backgroundColor = DARK_BLUE_COLOR;
 searchField.textColor = [UIColor whiteColor];
 searchField.font = [UIFont systemFontOfSize:16];
 searchField.leftView = self.leftView;
 if (@available(iOS 11.0, *)) {
  //Look at the view level. Before IOS 11, the X of searchBar was 12
  searchField.frame = CGRectMake(12, 8, SCREEN_WIDTH*0.8, 28);

 } else {
  searchField.frame = CGRectMake(0, 8, SCREEN_WIDTH*0.8, 28);
 }
 searchField.borderStyle = UITextBorderStyleNone;
 searchField.layer.cornerRadius = 5;
 searchField.layer.masksToBounds = YES;
 [searchField setValue:[UIColor whiteColor] forKeyPath:@"placeholderLabel.textColor"];
 [self setValue:searchField forKey:@"searchField"];
  self.searchTextPositionAdjustment  =(uioffset) {10, 0}; // cursor offset
}

That’s what we want.

The first page will stop for a while, and then we will start our search page. Different from the home page, the searchBar should be used in conjunction with the searchcontroller. Create a new ohsearchcontroller class and add a property


@property (nonatomic, strong) OHSearchBar *ohSearchBar;

setup code

- (instancetype)initWithSearchResultsController:(UIViewController *)searchResultsController searchBarFrame:(CGRect)searchBarFrame placeholder:(NSString *)placeholder textFieldLeftView:(UIImageView *)leftView showCancelButton:(BOOL)showCancelButton barTintColor:(UIColor *)barTintColor{
 if (self = [super initWithSearchResultsController:searchResultsController]) {
  self.ohSearchBar = [[OHSearchBar alloc] initWithFrame:searchBarFrame
             placeholder:placeholder
           textFieldLeftView:leftView
            showCancelButton:YES
             tintColor:barTintColor];
  
  UIButton *button = [self.ohSearchBar valueForKey:@"cancelButton"];
  button.tintColor = [UIColor whiteColor];
  [button settitle: @ "Cancel" forState:UIControlStateNormal ];
  [self.ohSearchBar setValue:button forKey:@"cancelButton"];
 }
 return self;
}

Then there is our view controller ohsearchviewcontroller

UIImageView *leftView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"search"]];
 leftView.bounds = CGRectMake(0, 0, 24, 24);
 self.ohSearchController = [[OHSearchController alloc] initWithSearchResultsController:self
                   searchBarFrame:CGRectMake(0, 0, SCREEN_WIDTH, 44)
                    Placeholder: @ "please enter the search content to search"
                  textFieldLeftView:leftView
                   showCancelButton:YES
                    barTintColor:BASE_BLUE_COLOR];
 
 [self.ohSearchController.ohSearchBar becomeFirstResponder];
 self.ohSearchController.ohSearchBar.delegate = self;
 [self.ohSearchController.ohSearchBar setLeftPlaceholder];
 self.navigationItem.titleView = self.ohSearchController.ohSearchBar;
 self.navigationItem.hidesBackButton = YES;

After completing this step, it is the interactive link. Click the searchBar on the home page to jump to the search page, and click the Cancel button on the search page to return to the home page. Home page set searchBar agent, and complete the agent method


- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
 OHSearchViewController *ohSearchViewController = [[OHSearchViewController alloc] init];
 [self.navigationController pushViewController:ohSearchViewController animated:NO];
 return YES;
}

Search page set searchBar agent, and complete the following proxy method

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
 [self.navigationController popViewControllerAnimated:NO];
}

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
 [self.ohSearchController.ohSearchBar resignFirstResponder];
 //Keep the Cancel button active
 UIButton *cancelBtn = [searchBar valueForKey:@"cancelButton"];
 cancelBtn.enabled = YES;
}

At this time, the problem appears again, click the Cancel button on the search page, instead of jumping back to the home page, it is still on this page. But you can see the screen flashing. Through printing the message, we found that by clicking the Cancel button, we executed the – (bool) searchbarshouldbeginediting: (UISearchBar *) searchBar method on the home page. After careful consideration, I found out that the reason is that the first responder was not cancelled. In addition, the interactive mechanism of the navigation bar did not refresh the page when pop arrived on the previous page, which caused this problem. The solution is to cancel the first responder when the home page needs to push the search page


- (void)viewWillDisappear:(BOOL)animated {
 [self.ohSearchBar resignFirstResponder];
}

Here we are. Can look at the source code to deepen understanding. I hope it will be helpful to your study, and I hope you can support developeppaer more.