Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A small extension for UISearchBar which shows an UIActivityIndicator while searching
//
// UISearchBar+Ext.swift
// frazeit
//
// Created by Maysam Shahsavari on 7/30/18.
// Updated on 9/26/19.
// Copyright © 2018 Maysam Shahsavari. All rights reserved.
//
import Foundation
import UIKit
extension UISearchBar {
private var textField: UITextField? {
let subViews = self.subviews.flatMap { $0.subviews }
if #available(iOS 13, *) {
if let _subViews = subViews.last?.subviews {
return (_subViews.filter { $0 is UITextField }).first as? UITextField
}else{
return nil
}
} else {
return (subViews.filter { $0 is UITextField }).first as? UITextField
}
}
private var searchIcon: UIImage? {
let subViews = subviews.flatMap { $0.subviews }
return ((subViews.filter { $0 is UIImageView }).first as? UIImageView)?.image
}
//////
private func getViewElement<T>(type: T.Type) -> T? {
let svs = subviews.flatMap { $0.subviews }
guard let element = (svs.filter { $0 is T }).first as? T else { return nil }
return element
}
func getSearchBarTextField() -> UITextField? {
return getViewElement(type: UITextField.self)
}
func setTextColor(color: UIColor) {
if let textField = getSearchBarTextField() {
textField.textColor = color
}
}
func setTextFieldColor(color: UIColor) {
if let textField = getViewElement(type: UITextField.self) {
switch searchBarStyle {
case .minimal:
textField.layer.backgroundColor = color.cgColor
textField.layer.cornerRadius = 6
case .prominent, .default:
textField.backgroundColor = color
@unknown default:
break
}
}
}
func setPlaceholderTextColor(color: UIColor) {
if let textField = getSearchBarTextField() {
textField.attributedPlaceholder = NSAttributedString(string: self.placeholder != nil ? self.placeholder! : "", attributes: [NSAttributedString.Key.foregroundColor: color])
}
}
///////
private var activityIndicator: UIActivityIndicatorView? {
return textField?.leftView?.subviews.compactMap{ $0 as? UIActivityIndicatorView }.first
}
// Public API
var isLoading: Bool {
get {
return activityIndicator != nil
} set {
let _searchIcon = searchIcon
if newValue {
if activityIndicator == nil {
let _activityIndicator = UIActivityIndicatorView(style: .gray)
_activityIndicator.startAnimating()
_activityIndicator.backgroundColor = UIColor.clear
let clearImage = UIImage().imageWithPixelSize(size: CGSize.init(width: 14, height: 14)) ?? UIImage()
self.setImage(clearImage, for: .search, state: .normal)
textField?.leftViewMode = .always
textField?.leftView?.addSubview(_activityIndicator)
let leftViewSize = CGSize.init(width: 14.0, height: 14.0)
_activityIndicator.center = CGPoint(x: leftViewSize.width/2, y: leftViewSize.height/2)
}
} else {
self.setImage(_searchIcon, for: .search, state: .normal)
activityIndicator?.removeFromSuperview()
}
}
}
}
/*
Usage:
To show the acttivity indicator
searchController.searchBar.isLoading = true
To stop the activity indicator (assuming it will be called when a network or extensive block is finished)
DispatchQueue.main.async {
searchController.searchBar.isLoading = false
}
*/
@johnjcamilleri

This comment has been minimized.

Copy link

johnjcamilleri commented Jan 24, 2019

Great extension, works like a charm!

@antoinepemeja

This comment has been minimized.

Copy link

antoinepemeja commented Mar 19, 2019

Yes great extension, thank you.
Unfortunately it doesn't work on iOS 10, it shows a square. Do you have an idea to fix it ?

@antoinepemeja

This comment has been minimized.

Copy link

antoinepemeja commented Mar 20, 2019

Hello,

My suggestion to support iOS 10 :

    var isLoading: Bool {
        get {
            
            return activityIndicator != nil
        } set {
            if newValue {
                if activityIndicator == nil {
                    
                    var style: UIActivityIndicatorView.Style = UIActivityIndicatorView.Style.gray
                    var backgroundColor: UIColor = UIColor.white
                    
                    if #available(iOS 11.0, *) {
                        style = UIActivityIndicatorView.Style.white
                        backgroundColor = UIColor.clear
                    }
                    
                    let _activityIndicator = UIActivityIndicatorView(style: style)
                    _activityIndicator.startAnimating()
                    _activityIndicator.backgroundColor = backgroundColor

                    if #available(iOS 11.0, *) {
                        self.setImage(UIImage(), for: .search, state: .normal)
                    }

                    textField?.leftView?.addSubview(_activityIndicator)
                    let leftViewSize = textField?.leftView?.frame.size ?? CGSize.zero
                    _activityIndicator.center = CGPoint(x: leftViewSize.width/2, y: leftViewSize.height/2)
                }
            } else {
                if #available(iOS 11.0, *) {
                    let _searchIcon = searchIcon
                    self.setImage(_searchIcon, for: .search, state: .normal)
                }
                activityIndicator?.removeFromSuperview()
            }
        }
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.