So in the screen recording, I want to create a search controller/search-bar like instacart has. I created a collection-view that has a search-bar in the navigation bar (via search controller). I want to show suggested searches (like instacarts' "Popular searches") when the search-bar is selected (instead of dimming the background and showing the collection-view - like its default function). And then once the user begins typing I want to have a table view that gets filtered and the filtered data gets presented (replacing the suggested searches) - like shown in the pictures. Ultimately I'm having trouble presenting suggested searches once the search-bar is clicked and presenting a filtered table once user begins typing in the search bar. Basically I would like to be able to do what instacart accomplished here. Any help would be greatly appreciated. Thanks!
Reference pictures
when you click the search bar from main category just navigate to the search suggestions.
In my case I used custom searchbar from UITextField.
I created 2 viewControllers 1 is for suggested results and other one is for showing search results.
in the searchsuggestionsVC viewDidload i am checking the character count and calling and loading suggested products in a table view as below
self.searchTF.addTarget(self, action: #selector(searchRecords(_ :)), for: .editingChanged)
create the searchRecords selecter as below
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if self.searchTF.text != "" {
//any task to perform
//if you want to dismiss your keyboard
print("Pro search keyboard button tapped")
self.proSearchQuery = self.searchTF.text ?? ""
UserDefaults.standard.set(self.proSearchQuery, forKey: "kProSearchQuery")
print("PRO SEARCH QUERY : \(self.proSearchQuery ?? "")")
self.navigatTo(identifier: "SearchResultsVC", animated: false)
}
else{
self.showCustomToast(message: "Please type something to search")
}
return true
}
And Used following delegates to navigate to the search results by tapping search button in the keyboard.
override func viewDidAppear(_ animated: Bool) {
self.searchTF.becomeFirstResponder()
}
func textFieldShouldClear(_ textField: UITextField) -> Bool {
print("text cleared")
return true
}
// search button execution
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if self.searchTF.text != "" {
//any task to perform
//if you want to dismiss your keyboard
print("Pro search keyboard button tapped")
self.proSearchQuery = self.searchTF.text ?? ""
UserDefaults.standard.set(self.proSearchQuery, forKey: "kProSearchQuery")
print("PRO SEARCH QUERY : \(self.proSearchQuery ?? "")")
self.navigatTo(identifier: "SearchResultsVC", animated: false)
}
else{
self.view.makeToast("Please type something to search")
}
return true
}
May be above code is not as per your requirements but hope you will get some Ideas on it. let me know in comments if anything not clear or add up.
Related
I am using swift in a storyboad application, and I would like it to, when a user enters the box (IE, when it becomes the first responder) to enter text it highlights all the text. What is done in most web browsers. Here is what I have tried, which has not worked:
func textFieldDidBeginEditing(_ textField: UITextField) {
// textField.selectAll(nil)
print("click 2")
// textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
print("click 1")
// textField.selectAll(nil)
// textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
return true
}
Making a button that runs this code does actually work, but I want it to be when you click on the field. Making an invisible button on top seems like a bad idea. I've tried other variations of this as well, but none have worked for me.
UPDATE: What I've ended up doing was making an invisible button on top of the existing text field, and then making that select the text. It may be a little janky but it works perfectly so I have no problems with that solution anymore. Extra code:
#IBAction func seachBtnClicked(_ sender: Any) {
//The invisible button over the search bar has been clicked
search.selectAll(nil)
searchBtn.isHidden = true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
//Return has been pressed, so we are going to load a new page
processInput()
//This does mean that it only comes back if you press enter,
//but right now I think that's how I want it.
//Could change it to be in the textFieldDidEndEditing() function to change that
searchBtn.isHidden = false
return true
}
You are probably just missing the textField.delegate = self for your textfield. The first of the two functions, func textFieldDidBeginEditing(_ textField: UITextField) is the one that is always performed as soon as a textfield is tapped on to focus on it.
Try adding yourTextField.delegate = self in your viewDidLoad function and see if that works out.
What I've ended up doing was making an invisible button on top of the existing text field, and then making that select the text. It may be a little janky but it works perfectly so I have no problems with that solution anymore. Extra code:
#IBAction func seachBtnClicked(_ sender: Any) {
//The invisible button over the search bar has been clicked
search.selectAll(nil)
searchBtn.isHidden = true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
//Return has been pressed, so we are going to load a new page
processInput()
//This does mean that it only comes back if you press enter,
//but right now I think that's how I want it.
//Could change it to be in the textFieldDidEndEditing() function to change that
searchBtn.isHidden = false
return true
}
I have a UIViewController which has 3 three UITextFields - TF1, TF2 and TF3. When the app is running on iOS simulator and I press Tab key, the focus moves to the next text field as expected.
TF3 is special. When the user taps on TF3, the text field should not get focused but instead a new view must be shown on the screen. I have implemented the UITextFieldDelegate method textFieldShouldBeginEditing(_:) to fix this as follows-
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if (textField == TF3) {
// show new view
return false
}
return true
}
This works as expected.
After the above code is added, whenever the Tab key is pressed, the new view is shown. This is because whenever the Tab key is pressed, textFieldShouldBeginEditing(_:) is called on all the text fields on the screen.
Consider that TF1 is the first responder at present.
If I press Tab key, it moves the focus to TF2 as expected. However, the new view is also shown because textFieldShouldBeginEditing(_:) is called on TF3.
Can anyone point out how to solve this issue?
if I understand you correctly,what you want is below:
Plan A
var isTF3Beginned = false // a property of current view controller as flag
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if (textField == TF3 && isTF3Beginned == true) {
// show new view
return false
}
isTF3Beginned = true
return true
}
Plan B RxSwift
TF3.rx
.controlEvent(.editingDidBegin)
.skip(1)
.subscribe(onNext: { () in
// show new view
}).disposed(by: disposeBag)
I'm learning Swift by making a times tables app, which simply creates random times tables and asks the user to type the answer in a UITextField.
I'd like the user to be able to tap the return key and have the text field clear their previous answer and have the keyboard remain in view. I've already set up the other behaviour I want, it's just I can't seem to find any similar questions to figure out how to clear, and do all this when there return key is tapped.
Here's the code I have so far, which works when dismissing the keyboard, and doesn't clear the field.
Many thanks!
#IBAction func answerTyped(_ sender: UITextField) {
/* Clear the text field*/
questionNumber += 1
attempted += 1
markQuestion(answer: answer)
newQuestion(awardLevel: currentLevel, questionNumber: questionNumber)
}
If you want the textField to be cleared on tapping return button you can use the textFieldShouldReturn delegate.
func textFieldShouldReturn(_ textField: UITextField) -> Bool { //delegate method
textField.text = "" // Clears text
//Do other things that you want to do when user taps return button
return true
}
Note that this wont dismiss the textField like how you asked and usually users are used to dismissing their textField using return. So if you ask me this is not good UX. And you need to have a mechanism for the user to dismiss the keyboard if needed like tap outside to dismiss or something. So i hope you have that sorted out.
Your viewController should have implemented the UITextFieldDelegate for this method work.
class YourViewController: UITextFieldDelegate {
var textField: UITextField! //Using IBOutlet or whatever
func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
}
}
you will need to make something, likely your view controller containing this text field into a uitextfieldelegate, then add the necessary delegate functions.
I think the function you want is TextField:DidEndEditing:Reason https://developer.apple.com/documentation/uikit/uitextfielddelegate/2352220-textfielddidendediting
Or you can just use
func textFieldShouldReturn(_ textField: UITextField) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
which is called every time user hits the return key. You would need to add your logic to clear the contents before the returns.
;) hi guys!
I want delete function in MultivaluedSection,but I don't want display delete icon in section's rows, something like this:
let section2 = MultivaluedSection(multivaluedOptions: .Delete, footer: "")
for _ in 1..<4 {
section2 <<< PickerInlineRow<String> {
$0.title = "Tap to select"
$0.value = "client"
$0.options = nameList
}
}
I only want user swipe row to delete it.
I try solve it by check in Eureka source code,but can't find any method or property to do this.
FIX:
oh!I notice this code in Eureka demo->MultivaluedOnlyDeleteController->viewDidLoad:
tableView.isEditing = false
But,it seem not working at first time.user must tap edit button to reset editing status.
what's wrong with it???
To fix this issue you only have to override the viewWillAppear viewController method
override func viewWillAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableView.isEditing = false
}
I'm a noob here and in iOS world. I am having trouble dismiss keyboard on a specific case in my very simple todo list iOS app.
I'd like the keyboard to get dismiss when user taps anywhere outside the current text field or the keyboard itself. So far, I got the keyboard dismisses just fine (thanks to you guys here in stack overflow) when user taps on the UITableView, or most element on my app. HOWEVER, when user taps on another UITextField, the keyboard does not go away.
FYI, here's the list of existing threads I researched so far but have yet to solve this issue.
1) How to dismiss keyboard iOS programmatically
2) Resigning First Responder for multiple UITextFields
3) Dismissing the First Responder/Keyboard with multiple Textfields
4) (a few more at least but I lost track :( )
Here's what I did so far:
(in viewDidLoad())
// Add 'tap' gesture to dismiss keyboard when done adding/editing to-do item
var tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "tapOutside:")
tap.cancelsTouchesInView = true
self.view.addGestureRecognizer(tap)
func tapOutside(tapOutside: UIGestureRecognizer) {
// Dismiss keyboard
self.view.endEditing(true)
}
#IBAction func EditingDidBegin(sender: UITextField) {
// Highlight the text field which user is editing
self.highlightTextField(sender, highlight: true)
}
#IBAction func EditingDidEnd(sender: UITextField) {
// Undo text field highlight
self.highlightTextField(sender, highlight: false)
self.view.endEditing(true) // try this option and not working
self.setEditing(false, animated: true) // try this option and not working
sender.resignFirstResponder() // try this option and not working
UIApplication.sharedApplication().becomeFirstResponder() // try this option and not working
... // below is my code to update the todo item
}
I also tried to print out all subviews.isFirstResponder() of my view. All of it return false. I also tried override touchesBegan of my UIViewController, and inside it just calls self.view.endEditing(true) and call its super's. This also does not work.
Please help. :(
TIA!
UPDATE:
You guys are awesome! :D I got it working now thanks to you guys. There were several mistakes / messed up as I'm learning new framework. So here's what I did.
1) I did not set UITextField delegate correctly.
Mistake: I ctrl-draged textfield in xcode and link my viewController as delegate and thought that should work out. I will still need to research and understand better why.
Solution: I removed that ctrl-drag link and explicitly call myTextField.delegate = self in tableView:cellForRowAtIndexPath. And that did it. Thanks #Sidewalker
2) Mistake: I have a mixed of textFieldShouldBeginEditing, etc. and #IBAction func EditingDidBegin. So I got myself into the situation where textFieldShouldBeginEditing got the call, but EditingDidBegin did not get call.
Solution: Once I set the delegate = self explicitly and stick with implementing textField... methods and not use any #IBAction for textField, things just work.
Here's one option... We're going to add a boolean flag to determine whether or not we're in a textField when an edit attempt for another textField begins
Make your class adhere to UITextFieldDelegate
class MyClass: UIViewController, UITextFieldDelegate
Don't forget to set the delegate, we'll add the flag as well
myTextField.delegate = self
var inField = false
Implement "textFieldShouldBeginEditing" and "textFieldDidBeginEditing"
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
if inField {
inField = false
return false
}
return true
}
func textFieldDidBeginEditing(textField: UITextField) {
inField = true
}
I prefer tracking things like this rather than identifying subviews as it allows the flag to be utilized elsewhere and cuts down code complexity.
Well the keyboard isn't going away because it doesn't expect to have to. The new UITextField is just becoming the first responder while the other resigns. If you don't want a textField to become the first responder if another is already, you're going to have to cut it off before it gets the chance to. I would try to implement textFieldShouldBeginEditing and figuring out the logic there.
I'm not in love with the way this looks but this should do something along those lines.
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
for subView in self.view.subviews{
if(subView.isKindOfClass(UITextField)){
if(subView.isFirstResponder()){
subView.resignFirstResponder();
return false;
}
}
}
return true;
}
First set all the UITextField (your are creating) delegate as self and create one UITextField member variable. Now implement "textFieldDidBeginEditing" delegate method and assign the textfield to your member UITextField variable. As given below
func textFieldDidBeginEditing(textField: UITextField) {
yourMemberVariable = textField;
}
So now whenever you want to dismiss the keyboard call the dismiss method on "yourMemberVariable" object. It should work !!
What I usually do is implementing this two method:
The first one add a UITapGestureRecognizer to the whole UIViewController view
func hideKeyboard() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
The second one just get called every time the user touch anywhere on the UIViewController's view
func dismissKeyboard() {
self.view.resignFirstResponder()
}
I add the first one to the viewDidLoad method of the UIViewController. Or better yet if you want to use that on all the app just make that an extension for your UIViewController.
How about doing this in viewController, It works for me
func dismissKeyboard() {
//All the textFields in the form
let textFields = [textField1, textField2, textField3, textField4, textField5]
let firstResponder = textFields.first(where: {$0.isFirstResponder ?? false })
firstResponder?.resignFirstResponder()
}