New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added search bar component #69
Changes from 5 commits
d7276c7
6534d6f
d79c600
3bb984e
c78bc9e
a2cec60
461e339
63c6edc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import UIKit | ||
import Bento | ||
import BentoKit | ||
import BentoKitPlaygroundSupport | ||
import PlaygroundSupport | ||
import StyleSheets | ||
|
||
let bundle = Bundle(for: Component.TextInput.self) | ||
let styleSheet = Component.Search.StyleSheet() | ||
.compose(\.searchBar.backgroundColor, UIColor.gray.withAlphaComponent(0.25)) | ||
.compose(\.searchBar.showsCancelButton, true) | ||
let component = Component.Search( | ||
placeholder: "Placeholder", | ||
keyboardType: .default, | ||
didBeginEditing: { _ in | ||
print("didBeginEditing") | ||
}, | ||
textDidChange: { _, text in | ||
print("textDidChange", text) | ||
}, | ||
cancelButtonClicked: { | ||
print("cancelButtonClicked") | ||
$0.endEditing(true) | ||
}, | ||
styleSheet: styleSheet | ||
) | ||
|
||
PlaygroundPage.current.liveView = renderInTableView(component: component) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<Timeline | ||
version = "3.0"> | ||
<TimelineItems> | ||
</TimelineItems> | ||
</Timeline> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import Bento | ||
import StyleSheets | ||
|
||
extension Component { | ||
public final class Search: AutoRenderable { | ||
public let configurator: (View) -> Void | ||
public let styleSheet: StyleSheet | ||
|
||
public init( | ||
placeholder: String? = nil, | ||
keyboardType: UIKeyboardType = .default, | ||
didBeginEditing: Optional<(UISearchBar) -> Void> = nil, | ||
textDidChange: Optional<(UISearchBar, String) -> Void> = nil, | ||
cancelButtonClicked: Optional<(UISearchBar) -> Void> = nil, | ||
styleSheet: StyleSheet = StyleSheet() | ||
) { | ||
self.configurator = { view in | ||
view.searchBar.placeholder = placeholder | ||
view.searchBar.keyboardType = keyboardType | ||
view.didBeginEditing = didBeginEditing | ||
view.textDidChange = textDidChange | ||
view.cancelButtonClicked = cancelButtonClicked | ||
} | ||
self.styleSheet = styleSheet | ||
} | ||
} | ||
} | ||
|
||
extension Component.Search { | ||
public final class View: BaseView, UISearchBarDelegate { | ||
|
||
let searchBar = UISearchBar() | ||
|
||
var textDidChange: Optional<(UISearchBar, String) -> Void> = nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided to follow the signature of the delegate method. also it makes it easier to do things like dismiss keyboard when cancel is pressed (for some reason is not a default behaviour) without jumping through the hoops. it can always be ignored if not needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fair enough |
||
var cancelButtonClicked: Optional<(UISearchBar) -> Void> = nil | ||
var didBeginEditing: Optional<(UISearchBar) -> Void> = nil | ||
|
||
public override init(frame: CGRect) { | ||
super.init(frame: frame) | ||
|
||
searchBar.add(to: self) | ||
.pinEdges(to: layoutMarginsGuide) | ||
|
||
searchBar.backgroundColor = .white | ||
searchBar.barTintColor = .white | ||
searchBar.backgroundImage = UIImage() | ||
searchBar.delegate = self | ||
} | ||
|
||
@available(*, unavailable) | ||
public required init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
public func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { | ||
self.didBeginEditing?(searchBar) | ||
} | ||
|
||
public func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { | ||
searchBar.endEditing(true) | ||
} | ||
|
||
public func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { | ||
self.textDidChange?(searchBar, searchText) | ||
} | ||
|
||
public func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { | ||
self.cancelButtonClicked?(searchBar) | ||
} | ||
} | ||
} | ||
|
||
extension Component.Search { | ||
public final class StyleSheet: BaseViewStyleSheet<View> { | ||
public struct SearchBar: StyleSheetProtocol { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be moved to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sergdort done 👍 |
||
public var backgroundColor: UIColor = .white | ||
public var cornerRadius: CGFloat = 10 | ||
public var height: CGFloat = 36 | ||
public var showsCancelButton: Bool = false | ||
public var searchTextPositionAdjustment: UIOffset = UIOffset.init(horizontal: 8, vertical: 0) | ||
public var keyboardType: UIKeyboardType = .default | ||
public var returnKeyType: UIReturnKeyType = .search | ||
public var enablesReturnKeyAutomatically: Bool = true | ||
|
||
public func apply(to element: UISearchBar) { | ||
element.height(height) | ||
element.setTextInputBackgroundColor(color: backgroundColor, | ||
height: height, | ||
cornerRadius: cornerRadius) | ||
element.showsCancelButton = showsCancelButton | ||
element.searchTextPositionAdjustment = searchTextPositionAdjustment | ||
element.keyboardType = keyboardType | ||
element.returnKeyType = returnKeyType | ||
element.enablesReturnKeyAutomatically = enablesReturnKeyAutomatically | ||
} | ||
} | ||
public var searchBar: SearchBar = SearchBar() | ||
|
||
public init() {} | ||
|
||
public override func apply(to element: View) { | ||
super.apply(to: element) | ||
searchBar.apply(to: element.searchBar) | ||
} | ||
} | ||
} | ||
|
||
extension UISearchBar { | ||
func setTextInputBackgroundColor(color: UIColor, height: CGFloat, cornerRadius: CGFloat) { | ||
// creates an image with rounded corners from background color | ||
let size = CGSize(width: cornerRadius * 2, height: height) | ||
let backgroundImage = UIGraphicsImageRenderer(size: size) | ||
.image { imageContext in | ||
let path = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: cornerRadius) | ||
|
||
imageContext.cgContext.beginPath() | ||
imageContext.cgContext.addPath(path.cgPath) | ||
imageContext.cgContext.closePath() | ||
imageContext.cgContext.clip() | ||
|
||
imageContext.cgContext.setFillColor(color.cgColor) | ||
imageContext.cgContext.fill(CGRect(origin: .zero, size: size)) | ||
} | ||
setSearchFieldBackgroundImage(backgroundImage, for: .normal) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import BentoKit | ||
import StyleSheets | ||
import UIKit | ||
@testable import BentoKitPlaygroundSupport | ||
|
||
final class SearchSnapshotTests: SnapshotTestCase { | ||
let placeholder = "Search for address or a postcode" | ||
|
||
override func setUp() { | ||
super.setUp() | ||
self.recordMode = false | ||
} | ||
|
||
func testSearch() { | ||
let styleSheet = Component.Search.StyleSheet() | ||
.compose(\.searchBar.backgroundColor, UIColor.gray.withAlphaComponent(0.25)) | ||
.compose(\.searchBar.showsCancelButton, true) | ||
|
||
let component = Component.Search(placeholder: placeholder, styleSheet: styleSheet) | ||
|
||
verifyComponentForAllSizes(component: component) | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dito