From 74cf9daa1a0d427e166f2b647feb1bf938d57d53 Mon Sep 17 00:00:00 2001 From: Boisney Philippe Date: Tue, 7 Mar 2017 09:01:19 +0100 Subject: [PATCH] Update Cocoapod Config --- ModernSearchBar.podspec | 21 + .../search.imageset/Contents.json | 23 + .../search.imageset/search.png | Bin 0 -> 449 bytes .../search.imageset/search@2x.png | Bin 0 -> 899 bytes .../search.imageset/search@3x.png | Bin 0 -> 1486 bytes Pod/Classes/ModernSearchBar.swift | 436 ++++++++++++++++++ Pod/Classes/ModernSearchBarCell.swift | 133 ++++++ Pod/Classes/ModernSearchBarDelegate.swift | 15 + Pod/Classes/ModernSearchBarIcon.swift | 25 + Pod/Classes/ModernSearchBarModel.swift | 27 ++ 10 files changed, 680 insertions(+) create mode 100755 ModernSearchBar.podspec create mode 100644 Pod/Assets.xcassets/search.imageset/Contents.json create mode 100644 Pod/Assets.xcassets/search.imageset/search.png create mode 100644 Pod/Assets.xcassets/search.imageset/search@2x.png create mode 100644 Pod/Assets.xcassets/search.imageset/search@3x.png create mode 100644 Pod/Classes/ModernSearchBar.swift create mode 100644 Pod/Classes/ModernSearchBarCell.swift create mode 100644 Pod/Classes/ModernSearchBarDelegate.swift create mode 100644 Pod/Classes/ModernSearchBarIcon.swift create mode 100644 Pod/Classes/ModernSearchBarModel.swift diff --git a/ModernSearchBar.podspec b/ModernSearchBar.podspec new file mode 100755 index 0000000..ca4f6d7 --- /dev/null +++ b/ModernSearchBar.podspec @@ -0,0 +1,21 @@ +Pod::Spec.new do |s| +s.name = "ModernSearchBar" +s.version = "1.0" +s.summary = "ModernSearchBar" +s.description = "The famous iOS search bar with auto completion feature implemented." +s.homepage = "https://github.com/PhilippeBoisney/ModernSearchBar" +s.license = 'APACHE' +s.author = { "PhilippeBoisney" => "phil.boisney@gmail.com" } +s.source = { :git => "https://github.com/PhilippeBoisney/ModernSearchBar.git", :tag => s.version } +s.platform = :ios, '8.0' +s.requires_arc = true + +# If more than one source file: https://guides.cocoapods.org/syntax/podspec.html#source_files +s.source_files = 'Pod/Classes/**/*' +s.resource_bundles = { + 'ModernSearchBar' => [ + 'Pod/Assets.xcassets' + ] +} + +end diff --git a/Pod/Assets.xcassets/search.imageset/Contents.json b/Pod/Assets.xcassets/search.imageset/Contents.json new file mode 100644 index 0000000..216ea4a --- /dev/null +++ b/Pod/Assets.xcassets/search.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "search.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "search@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "search@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Pod/Assets.xcassets/search.imageset/search.png b/Pod/Assets.xcassets/search.imageset/search.png new file mode 100644 index 0000000000000000000000000000000000000000..fcdef4e56c6d8131116ea68572a2477339b5afc4 GIT binary patch literal 449 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 = Array() + private var suggestionListFiltred: Array = Array() + + private var suggestionListWithUrl: Array = Array() + private var suggestionListWithUrlFiltred: Array = Array() + + private var choice: Choice = .normal + + //MARKS: VIEWS + private var suggestionsView: UITableView! + private var suggestionsShadow: UIView! + + //MARK: DELEGATE + var delegateModernSearchBar: ModernSearchBarDelegate? + + //PUBLICS OPTIONS + public var shadowView_alpha: CGFloat = 0.3 + + public var searchImage: UIImage! = ModernSearchBarIcon.Icon.search.image + + public var searchLabel_font: UIFont? + public var searchLabel_textColor: UIColor? + public var searchLabel_backgroundColor: UIColor? + + public var suggestionsView_maxHeight: CGFloat = UIScreen.main.bounds.height.divided(by: 2.5) + public var suggestionsView_backgroundColor: UIColor? + public var suggestionsView_contentViewColor: UIColor? + public var suggestionsView_separatorStyle: UITableViewCellSeparatorStyle = .none + public var suggestionsView_selectionStyle: UITableViewCellSelectionStyle = UITableViewCellSelectionStyle.none + + public var suggestionsView_searchIcon_height: CGFloat = 17 + public var suggestionsView_searchIcon_width: CGFloat = 17 + public var suggestionsView_searchIcon_isRound = true + + + //MARK: INITIALISERS + required public init(coder aDecoder: NSCoder) { + super.init(coder: aDecoder)! + setup() + } + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + init () { + super.init(frame: CGRect.zero) + setup() + } + + + private func setup(){ + self.delegate = self + self.isSuggestionsViewOpened = false + self.interceptOrientationChange() + } + + private func configureViews(){ + + ///Configure suggestionsView (TableView) + self.suggestionsView = UITableView(frame: CGRect(x: getSuggestionsViewX(), y: getSuggestionsViewY(), width: getSuggestionsViewWidth(), height: 0)) + self.suggestionsView.delegate = self + self.suggestionsView.dataSource = self + self.suggestionsView.register(ModernSearchBarCell.self, forCellReuseIdentifier: "cell") + self.suggestionsView.rowHeight = UITableViewAutomaticDimension + self.suggestionsView.estimatedRowHeight = 100 + self.suggestionsView.separatorStyle = self.suggestionsView_separatorStyle + if let backgroundColor = suggestionsView_backgroundColor { self.suggestionsView.backgroundColor = backgroundColor } + + ///Configure the suggestions shadow (Behing the TableView) + self.suggestionsShadow = UIView(frame: CGRect(x: getShadowX(), y: getShadowY(), width: getShadowWidth(), height: getShadowHeight())) + self.suggestionsShadow.backgroundColor = UIColor.black.withAlphaComponent(self.shadowView_alpha) + + ///Configure the gesture to handle click on shadow and improve focus on searchbar + let gestureShadow = UITapGestureRecognizer(target: self, action: #selector (self.onClickShadowView (_:))) + self.suggestionsShadow.addGestureRecognizer(gestureShadow) + + let gestureRemoveFocus = UITapGestureRecognizer(target: self, action: #selector (self.removeFocus (_:))) + gestureRemoveFocus.cancelsTouchesInView = false + self.getTopViewController()?.view.addGestureRecognizer(gestureRemoveFocus) + + ///Reload datas of suggestionsView + self.suggestionsView.reloadData() + } + + // -------------------------------- + // SET DATAS + // -------------------------------- + + public func setDatas(datas: Array){ + if (!self.suggestionListWithUrl.isEmpty) { fatalError("You have already filled 'suggestionListWithUrl' ! You can fill only one list. ") } + self.suggestionList = datas + self.choice = .normal + } + + public func setDatasWithUrl(datas: Array){ + if (!self.suggestionList.isEmpty) { fatalError("You have already filled 'suggestionList' ! You can fill only one list. ") } + self.suggestionListWithUrl = datas + self.choice = .withUrl + } + + // -------------------------------- + // DELEGATE METHODS SEARCH BAR + // -------------------------------- + + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + if searchText.isEmpty { + self.closeSuggestionsView() + } else { + self.searchWhenUserTyping(caracters: searchText) + self.openSuggestionsView() + } + self.delegateModernSearchBar?.searchBar?(searchBar, textDidChange: searchText) + } + + func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { + if self.suggestionsView == nil { self.configureViews() } + self.delegateModernSearchBar?.searchBarTextDidEndEditing?(searchBar) + } + + func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { + self.closeSuggestionsView() + self.delegateModernSearchBar?.searchBarTextDidEndEditing?(searchBar) + } + + func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { + self.closeSuggestionsView() + self.delegateModernSearchBar?.searchBarSearchButtonClicked?(searchBar) + self.endEditing(true) + } + + func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { + self.closeSuggestionsView() + self.delegateModernSearchBar?.searchBarCancelButtonClicked?(searchBar) + self.endEditing(true) + } + + + // -------------------------------- + // ACTIONS + // -------------------------------- + + ///Handle click on shadow view + func onClickShadowView(_ sender:UITapGestureRecognizer){ + self.delegateModernSearchBar?.onClickShadowView?(shadowView: self.suggestionsShadow) + self.closeSuggestionsView() + } + + ///Remove focus when you tap outside the searchbar + func removeFocus(_ sender:UITapGestureRecognizer){ + if (!isSuggestionsViewOpened){ self.endEditing(true) } + } + + // -------------------------------- + // DELEGATE METHODS TABLE VIEW + // -------------------------------- + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch self.choice { + case .normal: + return self.suggestionListFiltred.count + case .withUrl: + return self.suggestionListWithUrlFiltred.count + } + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + let cell:ModernSearchBarCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ModernSearchBarCell + + var title = "" + + switch self.choice { + case .normal: + title = self.suggestionListFiltred[indexPath.row] + break + case .withUrl: + title = self.suggestionListWithUrlFiltred[indexPath.row].title + break + } + + ///Configure label + cell.labelModelSearchBar.text = title + if let font = self.searchLabel_font { cell.labelModelSearchBar.font = font } + if let textColor = self.searchLabel_textColor { cell.labelModelSearchBar.textColor = textColor } + if let backgroundColor = self.searchLabel_backgroundColor { cell.labelModelSearchBar.backgroundColor = backgroundColor } + + ///Configure content + if let contentColor = suggestionsView_contentViewColor { cell.contentView.backgroundColor = contentColor } + cell.selectionStyle = self.suggestionsView_selectionStyle + + ///Configure Image + cell.configureImage(choice: self.choice, searchImage: self.searchImage, suggestionsListWithUrl: self.suggestionListWithUrlFiltred, position: indexPath.row, isImageRound: self.suggestionsView_searchIcon_isRound, heightImage: self.suggestionsView_searchIcon_height) + //self.fetchImageFromUrl(model: self.suggestionListWithUrlFiltred[indexPath.row], cell: cell, indexPath: indexPath) + + ///Configure max width label size + cell.configureWidthMaxLabel(suggestionsViewWidth: self.suggestionsView.frame.width, searchImageWidth: self.suggestionsView_searchIcon_width) + + ///Configure Constraints + cell.configureConstraints(heightImage: self.suggestionsView_searchIcon_height, widthImage: self.suggestionsView_searchIcon_width) + + + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + switch self.choice { + case .normal: + self.delegateModernSearchBar?.onClickItemSuggestionsView?(item: self.suggestionListFiltred[indexPath.row]) + case .withUrl: + self.delegateModernSearchBar?.onClickItemWithUrlSuggestionsView?(item: self.suggestionListWithUrlFiltred[indexPath.row]) + } + } + + // -------------------------------- + // SEARCH FUNCTION + // -------------------------------- + + ///Main function that is called when user searching + private func searchWhenUserTyping(caracters: String){ + + switch self.choice { + + ///Case normal (List of string) + case .normal: + + var suggestionListFiltredTmp = Array() + DispatchQueue.global(qos: .background).async { + for item in self.suggestionList { + if (self.researchCaracters(stringSearched: caracters, stringQueried: item)){ + suggestionListFiltredTmp.append(item) + } + } + DispatchQueue.main.async { + self.suggestionListFiltred.removeAll() + self.suggestionListFiltred.append(contentsOf: suggestionListFiltredTmp) + self.updateAfterSearch() + } + } + + break + + ///Case with URL (List of ModernSearchBarModel) + case .withUrl: + + var suggestionListFiltredWithUrlTmp = Array() + DispatchQueue.global(qos: .background).async { + for item in self.suggestionListWithUrl { + if (self.researchCaracters(stringSearched: caracters, stringQueried: item.title)){ + suggestionListFiltredWithUrlTmp.append(item) + } + } + DispatchQueue.main.async { + self.suggestionListWithUrlFiltred.removeAll() + self.suggestionListWithUrlFiltred.append(contentsOf: suggestionListFiltredWithUrlTmp) + self.updateAfterSearch() + } + } + + break + } + } + + private func researchCaracters(stringSearched: String, stringQueried: String) -> Bool { + return ((stringQueried.range(of: stringSearched, options: String.CompareOptions.caseInsensitive, range: nil, locale: nil)) != nil) + } + + private func updateAfterSearch(){ + self.suggestionsView.reloadData() + self.updateSizeSuggestionsView() + } + + // -------------------------------- + // SUGGESTIONS VIEW UTILS + // -------------------------------- + + private func openSuggestionsView(){ + if (!self.isSuggestionsViewOpened){ + self.animationOpening() + + self.addViewToParent(view: self.suggestionsShadow) + self.addViewToParent(view: self.suggestionsView) + self.isSuggestionsViewOpened = true + } + } + + private func closeSuggestionsView(){ + if (self.isSuggestionsViewOpened){ + self.animationClosing() + self.isSuggestionsViewOpened = false + } + } + + private func animationOpening(){ + UIView.animate(withDuration: 0.3, delay: 0.0, options: [], animations: { + self.suggestionsView.alpha = 1.0 + self.suggestionsShadow.alpha = 1.0 + }, completion: nil) + } + + private func animationClosing(){ + UIView.animate(withDuration: 0.3, delay: 0.0, options: [], animations: { + self.suggestionsView.alpha = 0.0 + self.suggestionsShadow.alpha = 0.0 + }, completion: { (finished) -> Void in + self.suggestionsView.removeFromSuperview() + self.suggestionsShadow.removeFromSuperview() + }) + } + + // -------------------------------- + // VIEW UTILS + // -------------------------------- + + private func getSuggestionsViewX() -> CGFloat { + return self.getEditText().frame.origin.x + } + + private func getSuggestionsViewY() -> CGFloat { + return self.frame.origin.y.adding(self.getEditText().frame.height).adding((self.frame.height.subtracting(self.getEditText().frame.height)).divided(by: 2)).subtracting(4) + } + + private func getSuggestionsViewWidth() -> CGFloat { + return self.getEditText().frame.width + } + + private func getSuggestionsViewHeight() -> CGFloat { + return self.getEditText().frame.height + } + + private func getShadowX() -> CGFloat { + return self.frame.origin.x + } + + private func getShadowY() -> CGFloat { + return self.frame.origin.y.adding(self.frame.height) + } + + private func getShadowHeight() -> CGFloat { + return (self.getTopViewController()?.view.frame.height)! + } + + private func getShadowWidth() -> CGFloat { + return self.frame.width + } + + private func updateSizeSuggestionsView(){ + var frame: CGRect = self.suggestionsView.frame + frame.size.height = self.getMaxHeightSuggestionsView(newHeight: self.suggestionsView.contentSize.height) + self.suggestionsView.frame = frame + UIView.animate(withDuration: 0.0) { + self.suggestionsView.layoutIfNeeded() + self.suggestionsView.sizeToFit() + } + } + + private func getMaxHeightSuggestionsView(newHeight: CGFloat) -> CGFloat { + if (newHeight > self.suggestionsView_maxHeight) { + return self.suggestionsView_maxHeight + } else { + return newHeight + } + } + + // -------------------------------- + // UTILS + // -------------------------------- + + private func addViewToParent(view: UIView){ + if let topController = getTopViewController() { + let superView: UIView = topController.view + superView.addSubview(view) + } + } + + private func getTopViewController() -> UIViewController? { + var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController + while topController?.presentedViewController != nil { + topController = topController?.presentedViewController + } + return topController + } + + private func getEditText() -> UITextField { + return self.value(forKey: "searchField") as! UITextField + } + + private func getText() -> String{ + if let text = self.getEditText().text { + return text + } else { + return "" + } + } + + private func interceptOrientationChange(){ + self.getEditText().addObserver(self, forKeyPath: "frame", options: NSKeyValueObservingOptions(rawValue: 0), context: nil) + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if (keyPath == "frame"){ + self.closeSuggestionsView() + self.configureViews() + } + } + + // -------------------------------- + // PUBLIC ACCESS + // -------------------------------- + + public func getSuggestionsView() -> UITableView { + return self.suggestionsView + } +} diff --git a/Pod/Classes/ModernSearchBarCell.swift b/Pod/Classes/ModernSearchBarCell.swift new file mode 100644 index 0000000..4333602 --- /dev/null +++ b/Pod/Classes/ModernSearchBarCell.swift @@ -0,0 +1,133 @@ +// +// ModernSearchBarCell.swift +// SearchBarCompletion +// +// Created by Philippe on 06/03/2017. +// Copyright © 2017 CookMinute. All rights reserved. +// + +import UIKit + +class ModernSearchBarCell: UITableViewCell { + + public static let defaultMargin: CGFloat = 10 + + let imgModernSearchBar = UIImageView() + var labelModelSearchBar = UILabel() + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + self.setup() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func awakeFromNib() { + super.awakeFromNib() + self.setup() + } + + private func setup(){ + ///Setup image + self.imgModernSearchBar.translatesAutoresizingMaskIntoConstraints = false + self.imgModernSearchBar.contentMode = .scaleAspectFit + + ///Setup label + self.labelModelSearchBar.translatesAutoresizingMaskIntoConstraints = false + self.labelModelSearchBar.numberOfLines = 0 + self.labelModelSearchBar.lineBreakMode = NSLineBreakMode.byWordWrapping + + self.contentView.addSubview(imgModernSearchBar) + self.contentView.addSubview(labelModelSearchBar) + } + + ///Configure constraint for each row of suggestionsView + public func configureConstraints(heightImage: CGFloat, widthImage: CGFloat){ + + ///Image constraints + let verticalImageConstraint = NSLayoutConstraint(item: self.imgModernSearchBar, attribute:.centerYWithinMargins, relatedBy: .equal, toItem: self.contentView, attribute: .centerYWithinMargins, multiplier: 1.0, constant: 0) + let topImageConstraint = NSLayoutConstraint(item: self.imgModernSearchBar, attribute:.top, relatedBy: .greaterThanOrEqual, toItem: self.contentView, attribute: .top, multiplier: 1.0, constant: ModernSearchBarCell.defaultMargin) + topImageConstraint.priority = UILayoutPriorityDefaultLow + let bottomImageConstraint = NSLayoutConstraint(item: self.imgModernSearchBar, attribute:.bottom, relatedBy: .lessThanOrEqual, toItem: self.contentView, attribute: .bottom, multiplier: 1.0, constant: ModernSearchBarCell.defaultMargin) + bottomImageConstraint.priority = UILayoutPriorityDefaultLow + let leftImageConstraint = NSLayoutConstraint(item: self.imgModernSearchBar, attribute:.left, relatedBy: .equal, toItem: self.contentView, attribute: .left, multiplier: 1.0, constant: ModernSearchBarCell.defaultMargin) + leftImageConstraint.priority = UILayoutPriorityDefaultHigh + let heightImageConstraint = NSLayoutConstraint(item: self.imgModernSearchBar, attribute:.height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: heightImage) + let widthImageConstraint = NSLayoutConstraint(item: self.imgModernSearchBar, attribute:.width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: widthImage) + + + ///Label constraints + let topLabelConstraint = NSLayoutConstraint(item: self.labelModelSearchBar, attribute:.top, relatedBy: .greaterThanOrEqual, toItem: self.contentView, attribute: .top, multiplier: 1.0, constant: ModernSearchBarCell.defaultMargin) + topLabelConstraint.priority = UILayoutPriorityDefaultHigh + let bottomLabelConstraint = NSLayoutConstraint(item: self.labelModelSearchBar, attribute:.bottom, relatedBy: .lessThanOrEqual, toItem: self.contentView, attribute: .bottom, multiplier: 1.0, constant: ModernSearchBarCell.defaultMargin) + bottomLabelConstraint.priority = UILayoutPriorityDefaultHigh + let verticalLabelConstraint = NSLayoutConstraint(item: self.labelModelSearchBar, attribute:.centerYWithinMargins, relatedBy: .equal, toItem: self.contentView, attribute: .centerYWithinMargins, multiplier: 1.0, constant: 0) + let leftLabelConstraint = NSLayoutConstraint(item: self.labelModelSearchBar, attribute:.left, relatedBy: .equal, toItem: self.imgModernSearchBar, attribute: .right, multiplier: 1.0, constant: ModernSearchBarCell.defaultMargin) + leftLabelConstraint.priority = UILayoutPriorityDefaultHigh + let leftLabelConstraintIfImageNil = NSLayoutConstraint(item: self.labelModelSearchBar, attribute:.left, relatedBy: .equal, toItem: self.contentView, attribute: .left, multiplier: 1.0, constant: ModernSearchBarCell.defaultMargin) + leftLabelConstraintIfImageNil.priority = UILayoutPriorityDefaultLow + let rightLabelConstraint = NSLayoutConstraint(item: self.labelModelSearchBar, attribute:.right, relatedBy: .equal, toItem: self.contentView, attribute: .right, multiplier: 1.0, constant: ModernSearchBarCell.defaultMargin) + rightLabelConstraint.priority = UILayoutPriorityDefaultLow + + + NSLayoutConstraint.activate([verticalImageConstraint, topImageConstraint, bottomImageConstraint, leftImageConstraint,heightImageConstraint, widthImageConstraint, + verticalLabelConstraint, leftLabelConstraint, rightLabelConstraint, topLabelConstraint, bottomLabelConstraint, leftLabelConstraintIfImageNil]) + } + + ///Configure image of suggestionsView + public func configureImage(choice: ModernSearchBar.Choice, searchImage: UIImage, suggestionsListWithUrl: Array, position: Int, isImageRound: Bool, heightImage: CGFloat){ + switch choice { + ///Show image from asset + case .normal: + self.imgModernSearchBar.image = searchImage + break + ///Show image from URL + case .withUrl: + let model = suggestionsListWithUrl[position] + if (model.imgCache != nil){ + self.imgModernSearchBar.image = model.imgCache + } else { + self.downloadImage(model: model) + } + break + } + + if (isImageRound){ + self.imgModernSearchBar.layer.cornerRadius = heightImage.divided(by: 2) + self.imgModernSearchBar.clipsToBounds = true + } + } + + ///Otherwise constaint on label doesn't works... + public func configureWidthMaxLabel(suggestionsViewWidth: CGFloat, searchImageWidth: CGFloat) { + var widthMax: CGFloat = suggestionsViewWidth + widthMax.subtract(ModernSearchBarCell.defaultMargin.multiplied(by: 3)) + widthMax.subtract(searchImageWidth) + self.labelModelSearchBar.preferredMaxLayoutWidth = widthMax + } + + //---------------------------- + + private func downloadImage(model: ModernSearchBarModel) { + DispatchQueue.global(qos: .background).async { + self.getDataFromUrl(url: model.url) { (data, response, error) in + guard let data = data, error == nil else { return } + let image = UIImage(data: data) + DispatchQueue.main.async() { () -> Void in + model.imgCache = image + self.imgModernSearchBar.image = image + } + } + } + } + + private func getDataFromUrl(url: URL, completion: @escaping (_ data: Data?, _ response: URLResponse?, _ error: Error?) -> Void) { + URLSession.shared.dataTask(with: url) { + (data, response, error) in + + completion(data, response, error) + }.resume() + } +} diff --git a/Pod/Classes/ModernSearchBarDelegate.swift b/Pod/Classes/ModernSearchBarDelegate.swift new file mode 100644 index 0000000..3f04c4a --- /dev/null +++ b/Pod/Classes/ModernSearchBarDelegate.swift @@ -0,0 +1,15 @@ +// +// ModernSearchBarDelegate.swift +// SearchBarCompletion +// +// Created by Philippe on 03/03/2017. +// Copyright © 2017 CookMinute. All rights reserved. +// + +import UIKit + +@objc protocol ModernSearchBarDelegate: UISearchBarDelegate { + @objc optional func onClickShadowView(shadowView: UIView) + @objc optional func onClickItemSuggestionsView(item: String) + @objc optional func onClickItemWithUrlSuggestionsView(item: ModernSearchBarModel) +} diff --git a/Pod/Classes/ModernSearchBarIcon.swift b/Pod/Classes/ModernSearchBarIcon.swift new file mode 100644 index 0000000..2f943a8 --- /dev/null +++ b/Pod/Classes/ModernSearchBarIcon.swift @@ -0,0 +1,25 @@ +// +// ModernSearchBarIcon.swift +// SearchBarCompletion +// +// Created by Philippe on 06/03/2017. +// Copyright © 2017 CookMinute. All rights reserved. +// + + +import UIKit + +public class ModernSearchBarIcon : NSObject { + + enum Icon { + case search + case none + + var image: UIImage { + switch self { + case .none: return UIImage() + case .search: return UIImage(named: "search")! + } + } + } +} diff --git a/Pod/Classes/ModernSearchBarModel.swift b/Pod/Classes/ModernSearchBarModel.swift new file mode 100644 index 0000000..0219565 --- /dev/null +++ b/Pod/Classes/ModernSearchBarModel.swift @@ -0,0 +1,27 @@ +// +// ModernSearchBarModel.swift +// SearchBarCompletion +// +// Created by Philippe on 06/03/2017. +// Copyright © 2017 CookMinute. All rights reserved. +// + +import UIKit + +class ModernSearchBarModel: NSObject { + + var title: String! + var url: URL! + var imgCache: UIImage! + + public init(title: String, url: String) { + super.init() + self.title = title + if let newUrl = URL(string: url) { + self.url = newUrl + } else { + print("ModernSearchBarModel: Seems url is not valid...") + self.url = URL(string: "#") + } + } +}