8
votes

I am working on Swift.

As regular SearchBar By default its appearing like Following Pic(1). enter image description here

But as per our design requirement I need to change it as Following Pic(2).

enter image description here

I tried using the following code I got like Following Pic(3)

enter image description here

**When I tried the following code the right view is getting hide and I could not able to see the text field rightview

Search icon completely invisible**

let searchField = (searchBar.valueForKey("_searchField") as! UITextField)
searchField.layer.cornerRadius = 15;


    let searchIcon = searchField.leftView!
    if (searchIcon is UIImageView) {
        print("yes")
    }

    searchField.rightView = searchIcon
  searchField.leftViewMode = .Never
searchField.rightViewMode = .Always

Can anyone help me to get the requirement. I can understand if you provide even on objective C as well

5

5 Answers

21
votes

In ViewController, leftview of searchbar textfield is the actual search icon, so make it to nil and create a image view with custom icon and set as a right view of search textfield.

Objective C

@interface FirstViewController ()
{
    UISearchBar  *searchBar;
}
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    searchBar = [[UISearchBar alloc] init];
    searchBar.frame = CGRectMake(15, 100, 350,50);

    [self.view addSubview:searchBar];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    UITextField *searchTextField = [((UITextField *)[searchBar.subviews objectAtIndex:0]).subviews lastObject];
    searchTextField.layer.cornerRadius = 15.0f;
    searchTextField.textAlignment = NSTextAlignmentLeft;

    UIImage *image = [UIImage imageNamed:@"search"];
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];

    searchTextField.leftView = nil;

    searchTextField.placeholder = @"Search";

    searchTextField.rightView = imageView;
    searchTextField.rightViewMode = UITextFieldViewModeAlways;
}

Swift

    private var searchBar: UISearchBar!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        searchBar = UISearchBar()
        searchBar.frame = CGRect(x: 15, y: 100, width: 350, height: 50)
        self.view.addSubview(searchBar)

    }

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

        let searchTextField:UITextField = searchBar.subviews[0].subviews.last as! UITextField
        searchTextField.layer.cornerRadius = 15
        searchTextField.textAlignment = NSTextAlignment.left
        let image:UIImage = UIImage(named: "search")!
        let imageView:UIImageView = UIImageView.init(image: image)
        searchTextField.leftView = nil
        searchTextField.placeholder = "Search"
        searchTextField.rightView = imageView
        searchTextField.rightViewMode = UITextFieldViewMode.always
    }

enter image description here

3
votes

I suggest you to check Apple documentation layoutIfNeeded() and layoutSubviews() . You'll have better understanding after reading them. The magic happens when you write searchBar.layoutIfNeeded() instance method. Another solution is searchBar.layoutSubviews() but Apple documentation says:

You should not call this method directly. If you want to force a layout update, call the setNeedsLayout() method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded() method.

According to @Jeyamahesan's answer. When you modify the code like below you do not need to separate the code in viewDidLoad() and ViewDidAppear().

Example:

import UIKit

class MainController: UIViewController {

    private var searchBar: UISearchBar!

    override func viewDidLoad() {
        super.viewDidLoad()

        searchBar = UISearchBar()
        searchBar.frame = CGRect(x: 15, y: 100, width: 350, height: 50)
        searchBar.layoutIfNeeded()
        self.view.addSubview(searchBar)

        let searchTextField:UITextField = searchBar.subviews[0].subviews.last as! UITextField
        searchTextField.layer.cornerRadius = 15
        searchTextField.textAlignment = NSTextAlignment.left
        let image:UIImage = UIImage(named: "Search")!
        let imageView:UIImageView = UIImageView.init(image: image)
        searchTextField.leftView = nil
        searchTextField.placeholder = "Search"
        searchTextField.rightView = imageView
        searchTextField.rightViewMode = UITextFieldViewMode.always
    }
}

My approach to solve the issue:

MainView.swift

import UIKit

class MainView: UIView {

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    private func setupUI() {
        backgroundColor = UIColor.lightGray
        setupSubViews()
        setupAutoLayout()
    }

    // MARK: Initialize UI Elements
    private let searchBar: UISearchBar = {
        let searchBar = UISearchBar()
        searchBar.isTranslucent = false
        searchBar.barTintColor = ColorPalette.LuminousDay.white
        searchBar.layoutIfNeeded()
        return searchBar
    }()

    private let searchImageView: UIImageView = {
        let image = UIImage(named: "Search")
        let imageView = UIImageView(image: image)
        imageView.contentMode = .scaleAspectFit
        return imageView
    }()

    private lazy var searchBarTextField: UITextField = { [unowned self] in
        let textField = self.searchBar.value(forKey: "searchBarTextField") as! UITextField
        textField.font = FontBook.SourceSansPro.Weight.semiBold.setFont(size: 24)
        textField.textColor = ColorPalette.LuminousDay.charm
        textField.tintColor = ColorPalette.LuminousDay.charm
        textField.clearButtonMode = .never
        textField.leftViewMode = .never
        textField.leftView = nil
        textField.rightViewMode = .always
        textField.rightView = self.searchImageView
        return textField
    }()

    private func setupSubViews() {
        addSubview(searchBar)
        searchBar.addSubview(searchBarTextField)
        searchBarTextField.addSubview(searchImageView)
    }

    private func setupAutoLayout() {
        searchBar.anchor(top: safeAreaLayoutGuide.topAnchor, bottom: nil,
                         leading: safeAreaLayoutGuide.leadingAnchor, trailing: safeAreaLayoutGuide.trailingAnchor,
                         centerX: nil, centerY: nil,
                         centerXconstant: nil, centerYconstant: nil,
                         size: CGSize.zero, padding: UIEdgeInsets(top: 50, left: 0, bottom: 0, right: 0))

        searchImageView.setSize(size: CGSize(width: 16, height: 16))
    }
}

MainController.swift

import UIKit

class MainController: UIViewController {

    override func loadView() {
        view = MainView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

    }
}
1
votes

Try This , and call on viewDidLayoutSubviews()

func SearchText(to searchBar: UISearchBar, placeHolderText: String) {
        searchBar.barTintColor = UIColor.clear
        searchBar.backgroundColor = UIColor.clear
        searchBar.isTranslucent = true
        searchBar.setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
        
        let searchTextField:UITextField = searchBar.value(forKey: "searchField") as? UITextField ?? UITextField()
        searchTextField.layer.cornerRadius = 15
        searchTextField.textAlignment = NSTextAlignment.left
        let image:UIImage = UIImage(named: "search")!
        let imageView:UIImageView = UIImageView.init(image: image)
        searchTextField.leftView = nil
         searchTextField.font = UIFont.textFieldText
        searchTextField.attributedPlaceholder = NSAttributedString(string: placeHolderText,attributes: [NSAttributedString.Key.foregroundColor: UIColor.yourCOlur])
        searchTextField.rightView = imageView
        searchTextField.backgroundColor = UIColor.yourCOlur
        searchTextField.rightViewMode = UITextField.ViewMode.always
    }
1
votes

Here is the complete search view customization with the storyboard in swift 5 and ios 13 or above

    @IBOutlet weak var searchview: UISearchBar!

    override func viewDidLoad() {
            super.viewDidLoad()
    
        //make background vertical  line
        searchview.backgroundImage = UIImage()
        
        if #available(iOS 13.0, *) {
                let searchTextField:UITextField = searchview.searchTextField
                searchTextField.textAlignment = .left
                searchTextField.layer.cornerRadius = 20
                searchTextField.layer.masksToBounds = true
                searchTextField.rightView = nil

                //your custom icon here
                let image:UIImage = UIImage(named: "Search")!
                let imageV:UIImageView = UIImageView.init(image: image)
                searchTextField.rightView = nil
                searchTextField.placeholder = "Search"
                searchTextField.leftView = imageV
                //dafault search icon
                let imageV = searchTextField.leftView as! UIImageView
                imageV.image = imageV.image?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
                imageV.tintColor = UIColor.lightGray
                
            }
    
 }
0
votes

Switf 5

Reference Jeyamahesan's answer。

I found viewDidLayoutSubviews is a better chance than viewDidAppear. If you call setupSearchTextField() in viewDidAppear, you will see a flash change on searchTextField.

Becasue viewDidLayoutSubviews will be called twice at first time, and be called many time later. so I add a isFirst variable.

R.string.localizable.product_name() -> It's a good resource framework, you can found more detail here. https://github.com/mac-cain13/R.swift

private var isFirst = true
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    guard isFirst else {
    return
    }
    isFirst = false
    setupSearchTextField()
}

private func setupSearchTextField() {
    let searchTextField = searchBar.searchTextField
    searchTextField.backgroundColor = .white
    searchTextField.layer.borderColor = UIColor(hex: "707070").cgColor
    searchTextField.layer.borderWidth = 1
    searchTextField.placeholder = R.string.localizable.product_name()
    let imageView = UIImageView(image: R.image.searchIcon())
    searchTextField.rightView = imageView
    searchTextField.rightViewMode = UITextField.ViewMode.always
    //delegate
    searchBar.delegate = self
}