91

I have my UISearchBar being part of the navigation bar like:

 let searchBar = UISearchBar()
 //some more configuration to the search bar
 .....
 navigationItem.titleView = searchBar

After updating to iOS 11 something weird happened to the search bar in my app. On iOS 10 and prior I had my navigation bar looking like:

enter image description here

Now with iOS 11 I have:

enter image description here

As you can see there is difference in the rounding of the two search bars which does not bothers me. The problem is that the search bar increases the height of the navigation bar. So when I go to another controller it looks weird too:

enter image description here

In fact that weird black line's height plus the current navigation bar's height is equal to the height of navigation bar shown in the second picture ...

Any ideas how to get rid of the black line and having consistent navigation bar height across all view controllers ?

radioaktiv
  • 2,437
  • 4
  • 27
  • 36
  • Possible duplicate of [iOS 11 SearchBar in NavigationBar](https://stackoverflow.com/questions/45350035/ios-11-searchbar-in-navigationbar) – Jakub Truhlář Nov 01 '17 at 15:15

19 Answers19

70

I got black line under NavigationBar with SearchBar in iOS 11 in two cases:

  • when i pushed another ViewControllers from ViewController with UISearchBar enter image description here

  • when i dismissed ViewController with UISearchBar with "drag right to dismiss" enter image description here

My solution was: adding this code to my ViewController with UISearchBar:

-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [self.navigationController.view setNeedsLayout]; // force update layout
    [self.navigationController.view layoutIfNeeded]; // to fix height of the navigation bar
}

Swift 4 Update

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    navigationController?.view.setNeedsLayout() // force update layout
    navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar
}
0x384c0
  • 2,256
  • 1
  • 17
  • 14
67

You can add a constraint of height 44 to the search bar for iOS 11.

// Swift

if #available(iOS 11.0, *) {
    searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
}

// Objective-C

if (@available(iOS 11.0, *)) {
    [searchBar.heightAnchor constraintEqualToConstant:44].active = YES;
}
Bruno Bieri
  • 9,724
  • 11
  • 63
  • 92
zgjie
  • 2,099
  • 2
  • 21
  • 32
  • Correct. This is how I managed to solved it actually. As well as calling `searchBar.sizeToFit()` in the `viewWillAppear`.If you add the call to `sizeToFit()` I will accept your answer :) . – radioaktiv Sep 22 '17 at 06:59
  • 1
    @radioaktiv I didn't write `sizeToFit()` and it works for me. – zgjie Sep 22 '17 at 07:33
  • But what exactly changed that caused this problem? – Iulian Onofrei Sep 27 '17 at 16:00
  • @IulianOnofrei it use auto layout now, not before. – zgjie Sep 27 '17 at 16:02
  • I mean, in iOS 11, what did Apple change to `UISearchBar`. – Iulian Onofrei Sep 27 '17 at 16:02
  • @IulianOnofrei made it’s height 56 in iOS 11 – zgjie Sep 27 '17 at 16:05
  • This works, but searchBar still has a 56 height. , – Chris Yim Sep 28 '17 at 02:51
  • 12
    Although this works for bringing the height back to normal, if I tap on my searchbar, for some reason it cuts off the bottom part of the searchbar. Any ideas why this happens or anyone else having this issue? – Christopher Smit Sep 29 '17 at 12:20
  • 3
    Here's the obj-c version: [searchBar.heightAnchor constraintEqualToConstant:44].active = YES; but one issue is that while this fixes the height issue, it reeks havoc on the rest of UISearchController's view. E.g., the bar buttons are hidden at first and the magnifying glass looks half drawn, or as if something is on top of it. Anyone else? – Bill Noto Oct 05 '17 at 21:35
  • 10
    Totally weird looking if you use this code because it just narrows the navigation bar but the height of the search bar is still 56. – OtakuFitness Oct 12 '17 at 06:09
  • 1
    This will create issue when you do this in landscape orientation. – Malav Soni Oct 13 '17 at 12:48
  • 4
    Did anyone else find a solution? doing this creates a lot of weird behavioral issues with the search bar. – Gustavo_fringe Oct 17 '17 at 15:30
  • 16
    This is not enough answer. When you click on the field, the size is still messed up. – Ross Oct 21 '17 at 19:48
  • Life saving answer! Size to fit not needed. If you have issues something else is wrong. – Jeff Oct 27 '17 at 17:10
  • I was able to get rid of the left nav button on top of the searchBar after loading by adding setNeedsUpdateConstraints/setNeedsLayout/layoutIfNeeded for navbar in viewDidLoad (searchBar is assigned to titleView in storyboard). The only way I found to fix layout constraints issues when rotating were in viewWillTransitionToSize:...set titleView to nil and in completion block of coordinator animateAlongsideTransition, re-set titleView to searchBar and update constraints/call setNeedsUpdateConstraints/setNeedsLayout/layoutIfNeeded for navbar. – tanya Nov 13 '17 at 19:33
  • This does not seem to make a `UISearchController` reappear which – in iOS11 – might be contained in the `UINavigationItem`. Do you have an idea how to do that? – DrMickeyLauer Apr 01 '18 at 12:14
  • 3
    I'm pretty sure in iOS 11.4 this doesn't work anymore. Apple sets a hard height constraint on the search bar. If you break it by setting `NSAutoresizingMaskLayoutConstraint = false`, Apple just enforces a `_UITemporaryLayoutHeight` to override you. – Ethan Allen Apr 20 '18 at 19:20
  • 7
    This doesnt work in iOS 13. Any workaround for this? – V V Oct 03 '19 at 18:20
44

I believe in iOS 11 UISearchBar now has the height equals to 56, and UINavigationBar uses autolayout to fit its subviews hence it increases the height. If you still want to have UISearchBar as titleView as in pre-iOS 11, I found out the best way to do it is to embed UISearchBar in a custom view, and set this view's height to 44, and assign it to navigationItem.titleView

class SearchBarContainerView: UIView {  

    let searchBar: UISearchBar  

    init(customSearchBar: UISearchBar) {  
        searchBar = customSearchBar  
        super.init(frame: CGRect.zero)  

        addSubview(searchBar)  
    }

    override convenience init(frame: CGRect) {  
        self.init(customSearchBar: UISearchBar())  
        self.frame = frame  
    }  

    required init?(coder aDecoder: NSCoder) {  
        fatalError("init(coder:) has not been implemented")  
    }  

    override func layoutSubviews() {  
        super.layoutSubviews()  
        searchBar.frame = bounds  
    }  
}  

class MyViewController: UIViewController {  

    func setupNavigationBar() {  
        let searchBar = UISearchBar()  
        let searchBarContainer = SearchBarContainerView(customSearchBar: searchBar)  
        searchBarContainer.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 44)  
        navigationItem.titleView = searchBarContainer  
    }  
} 
Mai Mai
  • 614
  • 5
  • 10
23

try this code on "ACKNOWLEDGEMENTS" view controller in viewDidLoad

self.extendedLayoutIncludesOpaqueBars = true
Silverwind
  • 231
  • 2
  • 4
  • Thanks! This is the solution I ended up going with, since I'm not using translucent bars. – mmh02 Jan 02 '18 at 23:00
  • Indeed this should be the answer. But I would like to add that place this piece of code in both the controller that over and below the search bar controller. – Amrit Sidhu Feb 10 '18 at 10:32
  • 1
    That worked for me, but a had to add navigationController?.view.layoutSubviews() in viewWillDisappear. – douglasd3 Feb 27 '18 at 14:58
  • This works very well, I would really appreciate if you just tell us what `extendedLayoutIncludesOpaqueBars ` property does. – Anirudha Mahale Sep 20 '18 at 04:24
  • extendedLayoutIncludesOpaqueBars expand your view (self.view) under the navigation and status bar. If you add a subview to the view(For example, UIImageView) and set it constraint to the top-left of the view and run the application, you will see the different when you set this property between true and false. – Silverwind Sep 21 '18 at 11:10
  • I have been finding solution since long time and finally I get the perfect solution. – Parthpatel1105 Dec 01 '18 at 16:39
  • Thank you so much for this! Still works for iOS 16.4 – Mishka Jul 03 '23 at 19:35
7

Thank you all! I finally found a solution.

Adding the following code to ViewController with UISearchBar.

  1. First step: viewDidLoad
-(void)viewDidLoad
{
    [super viewDidLoad];
    self.extendedLayoutIncludesOpaqueBars = YES;
    ...
}
override func viewDidLoad() {
    super.viewDidLoad()
    self.extendedLayoutIncludesOpaqueBars = true
}
  1. Second step:viewWillDisappear
-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
     // force update layout
    [self.navigationController.view setNeedsLayout]; 
    // to fix height of the navigation bar
    [self.navigationController.view layoutIfNeeded];  
}
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        navigationController?.view.setNeedsLayout() // force update layout
        navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar
    }
BlessNeo
  • 81
  • 1
  • 4
4

In Objective-C

if (@available(iOS 11.0, *)) {
        [self.searchBar.heightAnchor constraintLessThanOrEqualToConstant: 44].active = YES;
}              
iUser
  • 1,075
  • 3
  • 20
  • 49
Hassy
  • 5,068
  • 5
  • 38
  • 63
2

This happen to me too, all running well in iOS 12.4 and getting weird in 13 above. The problem is in iOS 13 navigation bar height increase from 88 to 100 after jump from UIViewController that implement searchBar.

Try this in your UIViewController that implement searchBar.

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    navigationController?.view.setNeedsLayout()
    navigationController?.view.layoutIfNeeded()
}

Preview after fixing:

enter image description here enter image description here

Preview before fixing:

enter image description here enter image description here

Avendi Sianipar
  • 261
  • 1
  • 9
1

EDIT: The @zgjie answer is a better solution for this problem: https://stackoverflow.com/a/46356265/1713123

It seems this happens because in iOS 11 the default height value of SearchBar was changed to 56, instead 44 on previous iOS versions.

For now, I've applied this workaround, setting searchBar height back to 44:

let barFrame = searchController.searchBar.frame
searchController.searchBar.frame = CGRect(x: 0, y: 0, width: barFrame.width, height: 44)    

Another solution could be use the new searchController property on navigationItem in iOS 11:

navigationItem.searchController = searchController

But this way da searchBar appears below navigation title.

alemorgado
  • 191
  • 2
  • 9
  • 1
    Is the change documented somewhere? I couldn't find it [here](https://developer.apple.com/documentation/uikit/uisearchbar?changes=latest_major). – Iulian Onofrei Sep 27 '17 at 16:21
  • 1
    FYI, you can modify the frame without making a variable for it; `CGRect().insetBy(dx:dy:)` and `CGRect().offsetBy(dx:dy:)`, in this case - `searchController.searchBar.frame = searchController.searchBar.frame.insetBy(dx: 0, dy: -12)` – Oscar Apeland Sep 28 '17 at 08:24
  • This solution worked but when you click the search bar the height is reset. – Gustavo_fringe Oct 10 '17 at 20:04
  • @Gustavo_fringe Did you find any solution for this. – Shivam Pokhriyal Jul 02 '19 at 12:53
1

All solution didn't work for me so before I pushed view controller I did:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    self.navigationItem.titleView = UIView()
}

And to make search bar present when going back:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    self.navigationItem.titleView = UISearchBar()
}
  • This removed the black view in the new viewcontroller but when returning to this viewcontroller(the one that contains searchBar), the rootView actually moved up a little bit. – code4latte Dec 21 '17 at 01:38
1

I couldn't use the solution of keeping the navBar at 44. So it took me a day but finally, I found a solution that doesn't change the bar height and position the button in the middle of the bar. The issue is that the buttons are placed in a stack view which is configured as Horizontal stack view and therefore doesn't adjust to the height change.

This is done on init:

UIBarButtonItem *cancelButton;
if (@available(iOS 11.0, *)) {
    // For iOS11 creating custom button to accomadate the change of navbar + search bar being 56 points
    self.navBarCustomButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.navBarCustomButton setTitle:@"Cancel"];
    [self.navBarCustomButton addTarget:self action:@selector(cancelButtonTapped) forControlEvents:UIControlEventTouchUpInside];
    cancelButton = [[UIBarButtonItem alloc] initWithCustomView:self.navBarCustomButton];
} else {
    cancelButton = [[UIBarButtonItem alloc] initWithTitle:MagicLocalizedString(@"button.cancel", @"Cancel")
                                                                                         style:UIBarButtonItemStylePlain
                                                                                        target:self
                                                                                        action:@selector(cancelButtonTapped)];
}

on viewWillApear (or anytime after the view was added to the navigation stack)

   if (@available(iOS 11.0, *)) {
        UIView *buttonsStackView = [navigationController.navigationBar subviewOfClass:[UIStackView class]];
        if (buttonsStackView ) {
            [buttonsStackView.centerYAnchor constraintEqualToAnchor:navigationController.navigationBar.centerYAnchor].active = YES;
            [self.navBarCustomButton.heightAnchor constraintEqualToAnchor:buttonsStackView.heightAnchor];
        }
    }

And subviewOfClass is a category on UIView:

- (__kindof UIView *)subviewOfClass:(Class)targetClass {
     // base case
     if ([self isKindOfClass:targetClass]) {
        return self;
     }

     // recursive
    for (UIView *subview in self.subviews) {
        UIView *dfsResult = [subview subviewOfClass:targetClass];

        if (dfsResult) {
           return dfsResult;
       }
   }
   return nil;
}
Zeev Vax
  • 914
  • 7
  • 13
1

All you have to do is to subclass UISearchBar and override "intrinsicContentSize":

@implementation CJSearchBar
-(CGSize)intrinsicContentSize{
    CGSize s = [super intrinsicContentSize];
    s.height = 44;
    return s;
}
@end

enter image description here

Jagie
  • 2,190
  • 3
  • 27
  • 25
1

Unable to comment, but wanted to share some additional issues I ran into while spending many hours trying to get to the bottom of this issue even after using one of the other solutions.

It appears the best fix for me was Andrew's answer:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    navigationController?.view.setNeedsLayout() // force update layout
    navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar
}

However, at the very least in iOS 12.1, if your UINavigationBar:

  • has isTranslucent set to false, the View Controller with the search bar appears to not get it's view's layout adjusted back when interactively dismissing (normal dismissing via back button appears to work).
  • has it's background image set using setBackgroundImage(UIImage(), for: .default), the transition animation doesn't work properly and will jump back to its position after finishing.

These particular properties were set to get the Navigation Bar to appear in a certain way however, so I need to do some adjusting to get it back, or put up with the weird behaviour. Will try to remember to update the above if I run into anything else or find other solutions or differences in other OS versions.

Ohifriend
  • 352
  • 4
  • 10
0

In my case, bigger UINavigationBar's height wasn't a problem for me. I just needed to realign left and right bar button items. That's the solution i've come up with:

- (void)iOS11FixNavigationItemsVerticalAlignment
{
    [self.navigationController.navigationBar layoutIfNeeded];

    NSString * currSysVer = [[UIDevice currentDevice] systemVersion];
    if ([currSysVer compare:@"11" options:NSNumericSearch] != NSOrderedAscending)
    {
        UIView * navigationBarContentView;
        for (UIView * subview in [self.navigationController.navigationBar subviews])
        {
            if ([subview isKindOfClass:NSClassFromString(@"_UINavigationBarContentView")])
            {
                navigationBarContentView = subview;
                break;
            }
        }

        if (navigationBarContentView)
        {
            for (UIView * subview in [navigationBarContentView subviews])
            {
                if (![subview isKindOfClass:NSClassFromString(@"_UIButtonBarStackView")]) continue;

                NSLayoutConstraint * topSpaceConstraint;
                NSLayoutConstraint * bottomSpaceConstraint;

                CGFloat topConstraintMultiplier = 1.0f;
                CGFloat bottomConstraintMultiplier = 1.0f;

                for (NSLayoutConstraint * constraint in navigationBarContentView.constraints)
                {
                    if (constraint.firstItem == subview && constraint.firstAttribute == NSLayoutAttributeTop)
                    {
                        topSpaceConstraint = constraint;
                        break;
                    }

                    if (constraint.secondItem == subview && constraint.secondAttribute == NSLayoutAttributeTop)
                    {
                        topConstraintMultiplier = -1.0f;
                        topSpaceConstraint = constraint;
                        break;
                    }
                }

                for (NSLayoutConstraint * constraint in navigationBarContentView.constraints)
                {
                    if (constraint.firstItem == subview && constraint.firstAttribute == NSLayoutAttributeBottom)
                    {
                        bottomSpaceConstraint = constraint;
                        break;
                    }

                    if (constraint.secondItem == subview && constraint.secondAttribute == NSLayoutAttributeBottom)
                    {
                        bottomConstraintMultiplier = -1.0f;
                        bottomSpaceConstraint = constraint;
                        break;
                    }
                }

                CGFloat contentViewHeight = navigationBarContentView.frame.size.height;
                CGFloat subviewHeight = subview.frame.size.height;
                topSpaceConstraint.constant = topConstraintMultiplier * (contentViewHeight - subviewHeight) / 2.0f;
                bottomSpaceConstraint.constant = bottomConstraintMultiplier * (contentViewHeight - subviewHeight) / 2.0f;
            }
        }
    }
}

Basically, we search for stack views that contain bar button items and then changing their's top and bottom constraints values. Yeah, it's a dirt hack, and won't recommend to use it if you can fix your issue in any other way.

0
//
//  Created by Sang Nguyen on 10/23/17.
//  Copyright © 2017 Sang. All rights reserved.
//

import Foundation
import UIKit

class CustomSearchBarView: UISearchBar {
    final let SearchBarHeight: CGFloat = 44
    final let SearchBarPaddingTop: CGFloat = 8
    override open func awakeFromNib() {
        super.awakeFromNib()
        self.setupUI()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setupUI()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
       // fatalError("init(coder:) has not been implemented")
    }
    func findTextfield()-> UITextField?{
        for view in self.subviews {
            if view is UITextField {
                return view as? UITextField
            } else {
                for textfield in view.subviews {
                    if textfield is UITextField {
                        return textfield as? UITextField
                    }
                }
            }
        }
        return nil;
    }
    func setupUI(){
        if #available(iOS 11.0, *) {
            self.translatesAutoresizingMaskIntoConstraints = false
            self.heightAnchor.constraint(equalToConstant: SearchBarHeight).isActive = true
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        if #available(iOS 11.0, *) {
            if let textfield = self.findTextfield() {
                textfield.frame = CGRect(x: textfield.frame.origin.x, y: SearchBarPaddingTop, width: textfield.frame.width, height: SearchBarHeight - SearchBarPaddingTop * 2)`enter code here`
                return
            }
        }
    }
}
0

I found Mai Mai's solution to be the only one that's really usable.
However it's still not perfect:
When rotating the device, the search bar is not properly resized and remains in the smaller dimension.

I have found a fix for that. Here is my code in Objective C with the relevant parts annotated:

// improvements in the search bar wrapper
@interface SearchBarWrapper : UIView
@property (nonatomic, strong) UISearchBar *searchBar;
- (instancetype)initWithSearchBar:(UISearchBar *)searchBar;
@end
@implementation SearchBarWrapper
- (instancetype)initWithSearchBar:(UISearchBar *)searchBar {
    // setting width to a large value fixes stretch-on-rotation
    self = [super initWithFrame:CGRectMake(0, 0, 4000, 44)];
    if (self) {
        self.searchBar = searchBar;
        [self addSubview:searchBar];
    }
    return self;
}
- (void)layoutSubviews {
    [super layoutSubviews];
    self.searchBar.frame = self.bounds;
}
// fixes width some cases of resizing while search is active
- (CGSize)sizeThatFits:(CGSize)size {
    return size;
}
@end

// then use it in your VC
@implementation MyViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.titleView = [[SearchBarWrapper alloc] initWithSearchBar:self.searchController.searchBar];
}
@end

Now there is still one case left that I haven't figured out yet. To reproduce do the following:
- start in portrait
- activate search field
- rotate to landscape
- error: the bar doesn't resize

de.
  • 7,068
  • 3
  • 40
  • 69
0

I fixed this by added the constraint to viewDidAppear on the map view controller where the search bar is embedded

public override func viewDidAppear(_ animated: Bool) {
    if #available(iOS 11.0, *) {

        resultSearchController?.searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
        // searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
    }
}
Akshay
  • 11
0

Hi to the people who uses UISearchController and then attaching its UISearchBar to the navigationItem.titleView. I've spend a crazy 4-5 hours of my day to solve this. Following the iOS 11+ recommended approach, which is putting the searchController to the navigation.searchController is not just right for my case. The screen that has this searchController/searchBar has a backButton, a custom one.

I have tested this in iOS 10, iOS 11, and 12. In different devices. I just had to. I can't go home without solving this demon. This is the most perfect I could do for today, given my tight deadline.

So I just wanna share this hard work that I did, it's up to you to put everything into where ever you want (ex. variables in your viewModel). Here it goes:

In my first screen (say home screen, that does not have this search controller), I have this in my viewDidLoad().

self.extendedLayoutIncludesOpaqueBars = true

In my second screen, the one that has the searchController, I have this in my viewDidAppear.

override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated)

    let systemMajorVersion = ProcessInfo.processInfo.operatingSystemVersion.majorVersion
    if systemMajorVersion < 12 {
        // Place the search bar in the navigation item's title view.
        self.navigationItem.titleView = self.searchController.searchBar
    }

    if systemMajorVersion >= 11 {

        self.extendedLayoutIncludesOpaqueBars = true

        UIView.animate(withDuration: 0.3) {
            self.navigationController?.navigationBar.setNeedsLayout()
            self.navigationController?.navigationBar.layoutIfNeeded()
        }

        self.tableView.contentInset = UIEdgeInsets(top: -40, left: 0, bottom: 0, right: 0)

        if self.viewHadAppeared {
            self.tableView.contentInset = .zero
        }
    }

    self.viewHadAppeared = true // this is set to false by default.
}

and here's my searchController's declaration:

lazy var searchController: UISearchController = {
    let searchController = UISearchController(searchResultsController: nil)
    searchController.hidesNavigationBarDuringPresentation = false
    searchController.dimsBackgroundDuringPresentation = false
    searchController.searchBar.textField?.backgroundColor = .lalaDarkWhiteColor
    searchController.searchBar.textField?.tintColor = .lalaDarkGray
    searchController.searchBar.backgroundColor = .white
    return searchController
}()

So I hope this helps someone someday.

Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95
-1

I tried various things to get the size back to the original 44, but then the search bar always looks and behaves weird - like being to far stretched, y-offset and alike.

I found a nice solution here (via some other stackoverflow post): https://github.com/DreamTravelingLight/searchBarDemo

Just derive your viewcontroller from the SearchViewController and include in your project the SearchViewController and WMSearchbar classes. Worked out of the box for me without any ugly if (iOS11) else... uglyness.

Martin Schultz
  • 2,571
  • 2
  • 26
  • 31
-1

In my case, I have to decrease the textField's height 36pt -> 28pt.

enter image description here

So I tried to change the frame's height, layer's height. But the ways didn't work.

Finally, I found a solution that's the mask. I think, It's not a good way but it works.

    let textField              = searchBar.value(forKey: "searchField") as? UITextField
    textField?.font            = UIFont.systemFont(ofSize: 14.0, weight: .regular)
    textField?.textColor       = #colorLiteral(red: 0.1960784314, green: 0.1960784314, blue: 0.1960784314, alpha: 1)
    textField?.textAlignment   = .left

    if #available(iOS 11, *) {
        let radius: CGFloat           = 5.0
        let magnifyIconWidth: CGFloat = 16.0
        let inset                     = UIEdgeInsets(top: 4.0, left: 0, bottom: 4.0, right: 0)

        let path = CGMutablePath()
        path.addArc(center: CGPoint(x: searchBar.bounds.size.width - radius - inset.right - magnifyIconWidth, y: inset.top + radius), radius: radius, startAngle: .pi * 3.0/2.0, endAngle: .pi*2.0, clockwise: false)                        // Right top
        path.addArc(center: CGPoint(x: searchBar.bounds.size.width - radius - inset.right - magnifyIconWidth, y: searchBar.bounds.size.height - radius - inset.bottom), radius: radius, startAngle: 0, endAngle: .pi/2.0, clockwise: false)  // Right Bottom
        path.addArc(center: CGPoint(x: inset.left + radius, y: searchBar.bounds.size.height - radius - inset.bottom), radius: radius, startAngle: .pi/2.0, endAngle: .pi, clockwise: false)                                                  // Left Bottom
        path.addArc(center: CGPoint(x: inset.left + radius, y: inset.top + radius),  radius: radius, startAngle: .pi, endAngle: .pi * 3.0/2.0, clockwise: false)                                                                             // Left top

        let maskLayer      = CAShapeLayer()
        maskLayer.path     = path
        maskLayer.fillRule = kCAFillRuleEvenOdd

        textField?.layer.mask = maskLayer
    }

You can change the insets, if you want to change the textField's frame.

Den
  • 3,179
  • 29
  • 26