Last active
March 14, 2022 01:07
-
-
Save maysamsh/fd06dd7bb19281229e89116697ebbcb6 to your computer and use it in GitHub Desktop.
A small extension for UISearchBar which shows an UIActivityIndicator while searching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// UISearchBar+Ext.swift | |
// frazeit | |
// | |
// Created by Maysam Shahsavari on 7/30/18. | |
// Updated on 9/26/19. | |
// Copyright © 2018 Maysam Shahsavari. All rights reserved. | |
// Updated: 10/02/2020. | |
import Foundation | |
import UIKit | |
extension UIImage { | |
func imageWithPixelSize(size: CGSize, filledWithColor color: UIColor = UIColor.clear, opaque: Bool = false) -> UIImage? { | |
return imageWithSize(size: size, filledWithColor: color, scale: 1.0, opaque: opaque) | |
} | |
func imageWithSize(size: CGSize, filledWithColor color: UIColor = UIColor.clear, scale: CGFloat = 0.0, opaque: Bool = false) -> UIImage? { | |
let rect = CGRect.init(x: 0, y: 0, width: size.width, height: size.height) | |
UIGraphicsBeginImageContextWithOptions(size, opaque, scale) | |
color.set() | |
UIRectFill(rect) | |
let image = UIGraphicsGetImageFromCurrentImageContext() | |
UIGraphicsEndImageContext() | |
return image | |
} | |
} | |
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.first { $0 is UITextField }) as? UITextField | |
} else { | |
return nil | |
} | |
} else { | |
return (subViews.first { $0 is UITextField }) as? UITextField | |
} | |
} | |
private var searchIcon: UIImage? { | |
let subViews = subviews.flatMap { $0.subviews } | |
return ((subViews.first { $0 is UIImageView }) as? UIImageView)?.image | |
} | |
private func getViewElement<T>(type: T.Type) -> T? { | |
let svs = subviews.flatMap { $0.subviews } | |
guard let element = (svs.first { $0 is T }) 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 | |
if #available(iOS 13.0, *) { | |
_activityIndicator = UIActivityIndicatorView(style: .medium) | |
} else { | |
_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 | |
} | |
*/ |
@maysamsh yes, I tried on Swift 5 with Xcode 11.2.1, on Simulator running iOS 13.2. However after the search icon replaced with the indicator, the search icon disappears from the left side of search field's text field.
First all, thanks for the gist and the great solution. I will like to suggest some improvements:
To remove the warning on iOS 13 and use the correct style:
if activityIndicator == nil {
let _activityIndicator: UIActivityIndicatorView
if #available(iOS 13.0, *) {
_activityIndicator = UIActivityIndicatorView(style: .medium)
} else {
_activityIndicator = UIActivityIndicatorView(style: .gray)
}
use first instead of filter
private var textField: UITextField? {
let subViews = self.subviews.flatMap { $0.subviews }
if #available(iOS 13, *) {
if let _subViews = subViews.last?.subviews {
return (_subViews.first { $0 is UITextField }) as? UITextField
} else {
return nil
}
} else {
return (subViews.first { $0 is UITextField }) as? UITextField
}
}
private var searchIcon: UIImage? {
let subViews = subviews.flatMap { $0.subviews }
return ((subViews.first { $0 is UIImageView }) as? UIImageView)?.image
}
private func getViewElement<T>(type: T.Type) -> T? {
let svs = subviews.flatMap { $0.subviews }
guard let element = (svs.first { $0 is T }) as? T else { return nil }
return element
}
remove force
private func setPlaceholderTextColor(color: UIColor) {
if let textField = getSearchBarTextField() {
textField.attributedPlaceholder = NSAttributedString(string: self.placeholder ?? "", attributes: [NSAttributedString.Key.foregroundColor: color])
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@onursahindur Have you tried the new code?