Various searchbars

The native UISearchBar has undergone a change in iOS 11, with the height being 56 instead of 44 (the default height estimates are all broken), and some minor changes to the style, such as rounded corners when not typed, and the text for magnifying glass ICONS and text is no longer centered to the left. Specific look at the picture

Some mainstream apps also embed searchBar in the navigation bar. Take netease Cloud Music and Zhihu as examples. The left side is the home page and the right side is the search page (note the cursor).

Implementation ideas and cases

The idea is to set the navigation bar titleView and the left and right BarButtonItems. There are three main ways

  • The titleView of the navigation bar of the home page is realized by button, and the search page is realized by searchBar.
  • The titleView for both the homepage and the navigation bar of the search page uses the searchBar, and the searchBar style is modified differently for the two pages. This way we can reuse our customized searchBar and reduce redundancy.
  • The navigation bar titleView of the home page uses button to achieve, and the search page uses textField. This approach is more thorough, more flexible, and relatively more complex.

Why does the titleView above say “Button” and not something else? Other things can certainly be done. Button comes with imageView and titleLabel, just setting offsets makes it easier to achieve what we want, with fewer view levels and more fluency.

case

In actual combat

Implement a navigation bar that looks like this by customizing the SearchBar

- (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; // It replaces the magnifying glass on the left
        [self setImage:[UIImage imageNamed:@"clear"] forSearchBarIcon:UISearchBarIconClear state:UIControlStateNormal]; // Replace the clearIcon on the right side of the input
    }
    return self;
}
Copy the code

Create a home page OHHomeViewController and set the navigation bar titleView and rightBarButton

// 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; 
Copy the code

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

Several differences can be seen at this point

  • The height of the searchBar
  • SearchBar textField magnifier and text position
  • TextField’s rounded corners are inconsistent
  • More carefully, you can also see that textField positions are inconsistent

Solution: First and second, determine whether the device is iOS 11, if it is, set its height to the left, if it is not. The key code is 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]; }}Copy the code

For the third and fourth questions, get the textField with KVC and customize it. Make textField position, size, and rounded corners 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
}
Copy the code

Again, let’s look at the runtime

I thought it was gonna be okay, but it’s a fucking mess, you know

TextFild has different lengths, different positions, different rounded corners to explain what’s going on here

  • If you look at the searchBar above the image, you will see that the textField has rounded corners to the left and a right Angle to the right, indicating that it is truncated. The scope of the navigation bar titleView is divided into that section, and the searchBar below even the rightBarButton, directly preempted the position. Speculated that this is due to the iOS 11 navigation view level change, can see here www.jianshu.com/p/352f101d6… , or their own popular science, not detailed expansion. So be careful with the searchBar size Settings and try to keep them in the right range.

  • TextField’s rounded corners are inconsistent. When you customize the size of the rounded corners, cancel its own rounded style

    searchField.borderStyle = UITextBorderStyleNone;
    Copy the code
  • If you look at the view hierarchy, you can see that below iOS 11, when you set titleView, the default coordinate for x is 12, and iOS 11 is 0. So if you set the x coordinate of the textField, in iOS 11 you have to add 12 more to make it a consistent position.

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 hierarchy. 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
}
Copy the code

This is where we want it to be.

The home page is over for the time being, and we can start our search page. Unlike the home page, the searchBar is used in conjunction with the searchController. Create a new OHSearchController class and add a property

@property (nonatomic.strong) OHSearchBar *ohSearchBar;
Copy the code

Initialization 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;
}
Copy the code

And then 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;
Copy the code

After this step, it is time to interact. 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. On the home page, set up the proxy for searchBar and complete the proxy methods

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

The search page sets up the proxy for the SearchBar and completes the proxy methods

- (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;
}
Copy the code

When you click the cancel button on the search page, you are still on the same page instead of returning to the home page. But you can see the flicker on the screen. Print a message found, point the cancel button, execute the homepage – (BOOL) searchBarShouldBeginEditing searchBar method: (UISearchBar *). After careful consideration, I figured out that the reason was that the first responder was not cancelled, and the interactive mechanism of the navigation bar, and the page would not be refreshed when pop to the previous page, which led to this problem. The solution is to cancel the first responder when the home page is about to push the search page

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

At this point, we are done. Can look at the source code to deepen understanding. The project source portal: HasjOH/OHSearchBarInNaviBar