Using a Textfield as a Search Bar in Swift 3 Xcode 8 - ios

I have my project setup like this:
I have a custom Tab Navigation Controller. In the top bar of the Custom Tab Navigation Controller is a text field. This text field changes according to the 4 main views of the app (4 tab buttons).
What I am trying to do is use the text field as a search bar by passing whatever is typed into the text field into the searchView However, I am having trouble passing the textfield.text from the Navigation Controller into searchView. I have a picture below that illustrates this more clearly.
The searchView has the search function taken care of. All I am trying to do is pass with textfieled.text value to the searchView whenever it is changed

// global
let handleTextChangeNotification = "handleTextChangeNotification"
your FirstViewController
override func viewDidLoad() {
super.viewDidLoad()
textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
}
func textFieldDidChange(textField: UITextField){
NotificationCenter.default.post(name: NSNotification.Name(rawValue: handleTextChangeNotification), object: nil, userInfo: ["text":textField.text])
let search_screen = SearchViewController()
self.navigationController?.pushViewController(search_screen, animated: true)
}
SeacrchViewController's
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(SeacrchViewController.handleTextChange(_:)),
name: NSNotification.Name(rawValue: handleTextChangeNotification),
object: nil)
}
func handleTextChange(_ myNot: Notification) {
if let use = myNot.userInfo {
if let text = use["text"] {
// your search with 'text' value
}
}
}
}

You can do something like that.
In your FirstViewController
func textFieldDidChange(textField: UITextField){
let search_screen = SearchViewController()
search_screen.search_string = self.textField.text
self.navigationController?.pushViewController(search_screen, animated: true)
}
In case of storyboards
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier == "Your_SearchViewController_identifier") {
let search_screen = segue.destinationViewController as!
SearchViewController
search_screen.search_string = self.textField.text
}
}
And In your SeacrchViewController's viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.textField.text = self.search_string
}

Related

How to reloadData() in ViewController #1 after ViewController #2 is dismissed?

i am new to iOS here is my question:
I have a saveCardViewController (Presented Modally) with some textFields and Save button.
#IBAction func Save(_ sender: UIButton) {
date = datePicker.date
try! realm.write() {
sessionCard.pokerType = pokerTypeSegment.titleForSegment(at: pokerTypeSegment.selectedSegmentIndex)!
date = dateFormatter.string(from: date)
sessionCard.handsPlayed = Int(handPlayedTextlabel.text!) ?? 0
sessionCard.moneyIn = Int(moneyInTextLabel.text!) ?? 0
sessionCard.moneyOut = Int(moneyOutTextLabel.text!) ?? 0
sessionCard.timePlayed = Int(timePlayedTextLabel.text!) ?? 0
sessionCard.sortDate = date
realm.add(sessionCard)
}
dismiss(animated: true, completion: nil)
}
How can I reloadData() on my main ViewController, after Save button is pressed and saveCardViewController is dismissed.
Thanks!
EDIT # 1:
Thank you #davidev for your answer,I made changes but still does not update
My ViewController With TableView:
class SessionViewController: BackgroundViewController, RefreshViewDelegate {
func refreshView() {
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
My ViewController with data and Save button:
protocol RefreshViewDelegate {
func refreshView()
}
class AddSessionViewController: UIViewController, UITextFieldDelegate {
var delegate: RefreshViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func TEST2(_ sender: UIButton) {
delegate?.refreshView()
dismiss(animated: true, completion: nil)
}
You can use delegate pattern to achieve this.
Declare your Refresh Protocol like this:
protocol RefreshViewDelegate {
func refreshView()
}
Make your parent view conform to this protocol and implement refreshView() with your custom refresh action. Also make sure to set the delegate of the child view to self.
Inside saveCardViewController declare your delegate variable
var delegate : RefreshViewDelegate?
And call the delegate action inside your IBaction
delegate?.refreshView()
Edit:
I just saw your updated code. As you are using Storyboard segues, you still have to set the delegate via code. In your main view controller add the function:
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
if let viewController = segue.destinationViewController as? AddSessionViewController
{
viewController.delegate = self
}
}

How to hide a UILabel from another UIViewController using Notifications and Observer

I have 2 UIViewControllers and I try to hide an UILabel from the second UIViewController using Notifications and Observer.
Is the first time when I use this design pattern and I'm a little bit confused. What I'm doing wrong ?
I want to specify that I'm getting the message from that print for the first time only when I click the back button from the second ViewController.
And after that I'm getting the message normal when I click Go Next but the UILabel is not hidden or colour changed.
Here is my code for first UIViewController:
class ReviewPhotosVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.post(name: Notification.Name("NotificationOfReviewMode"), object: nil)
}
#IBAction func goNextTapped(_ sender: UIButton) {
let fullscreenVC = storyboard?.instantiateViewController(withIdentifier: "FullscreenPhoto") as! FullscreenPhotoVC
self.present(fullscreenVC, animated: true, completion: nil)
}
}
Here is my code for second UIViewController:
class FullscreenPhotoVC: UIViewController {
#IBOutlet weak var customLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(hideCustomLabel),
name: Notification.Name("NotificationOfReviewMode"),
object: nil)
}
#IBAction func goBackTapped(_ sender: UIButton) {
let reviewPhotosVC = storyboard?.instantiateViewController(withIdentifier: "ReviewPhotos") as! ReviewPhotosVC
self.present(reviewPhotosVC, animated: true, completion: nil)
}
#objc func hideCustomLabel(){
customLabel.isHidden = true
customLabel.textColor = .red
print("My func was executed.")
}
}
Here is my Storyboard:
Thanks if you read this.
The problem is that you are posting the notification before the next controller is initialised and has started observing. Also, there is no need for the notification you can do it directly. In this case I have used an extra variable shouldHideLabel as you cannot call the function hideCustomLabel() directly because this will lead to crash as the outlets are only initialised after view is loaded.
class ReviewPhotosVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//NotificationCenter.default.post(name: Notification.Name("NotificationOfReviewMode"), object: nil)
}
#IBAction func goNextTapped(_ sender: UIButton) {
let fullscreenVC = storyboard?.instantiateViewController(withIdentifier: "FullscreenPhoto") as! FullscreenPhotoVC
fullscreenVC.shouldHideLabel = true
self.present(fullscreenVC, animated: true, completion: nil)
}
}
class FullscreenPhotoVC: UIViewController {
var shouldHideLabel = false
#IBOutlet weak var customLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if shouldHideLabel {
hideCustomLabel()
}
/*
NotificationCenter.default.addObserver(self,
selector: #selector(hideCustomLabel),
name: Notification.Name("NotificationOfReviewMode"),
object: nil)
*/
}
#IBAction func goBackTapped(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
#objc func hideCustomLabel() {
customLabel.isHidden = true
customLabel.textColor = .red
print("My func was executed.")
}
}

Make a UISwitch hide/unhide a label when it toggles on/off

MeditationSettingsViewController has a UISwitch which is linked to MeditationScreenViewController though a Segue. The UISwitch doesn't hide the text in the label called phaselabel from MeditationScreenViewController but instead displays the MeditationSettingsViewController screen. How do I get it so that the switch doesn't do this but hides and unhides phaselabel when the switch is turned on/off?
class MeditationSettingsViewController: UIViewController {
#IBAction func showCycleTitleChanged(_ sender: UISwitch) {
if (sender.isOn == true)
{
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segue" {
if let sendToDetailViewController = segue.destination as? MeditationScreenViewController {
sendToDetailViewController.isSwitchOn = sender!.isOn
}
}
class MeditationScreenViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
if isSwitchOn == true {
//unhide the label
self.phaseLabel.isHidden = true
//set your label value here
}
else {
self.phaseLabel.isHidden = false
}
}
Try using NSNotificationCenter to let the two view controllers be aware of the switch state change.
In MeditationSettingsViewController in the showCycleTitleChanged function, do this:
#IBAction func showCycleTitleChanged(_ sender: UISwitch) {
let data:[String: Bool] = ["state": sender!.isOn]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "switchChanged"), object: nil, userInfo: data)
}
In MeditationScreenViewController, listen to the notification like so:
In viewDidLoad:
NotificationCenter.default.addObserver(self, selector: #selector(self.showHideLabel(_:)), name: NSNotification.Name(rawValue: "switchChanged"), object: nil)
Also add this function to handle the notification:
func showHideLabel(_ notification: NSNotification) {
self.phaselabel.isHidden = notification.userInfo?["state"] as? Bool
}

How can I access IBOutlet in another class?

I am having the same error that is in this question: how can i access IBOutlet in another class? in swift but when I write in my Xcode (for iOS 8 with Swift 3) I have an error.
My code is this. I want to edit amauntOut (is an UILabel) that is in the class Convert_Table_Third_Cell with the action of one button:
#IBAction func actionTextb(_ sender: Any) {
print("you writted \(String(describing: amauntEnter.text!))----")
//Convert_Table_Third_Cell.amauntOut.text = amauntEnter.text ----> is a tried
//var dash : abcViewController = storyBoard.instantiateViewControllerWithIdentifier("abc") as! abcViewController ----> is a tried
//var a = dash.getXYZ() ----> is a tried
var update: Convert_Table_Third_Cell = UIStoryboard.instantiateViewController(UIStoryboard) as! Convert_Table_Third_Cell
update.amauntOut.text = "hola"
}
I get this error:
Instance member 'instantiateViewController' cannot be used on type 'UIStoryboard'; did you mean to use a value of this type instead?
Can someone help me?
this is the first class
import UIKit
class Convert_Table_Second_Cell: UITableViewCell {
#IBOutlet var amauntEnter: UITextField!
var theNumber = getTheNumber()
#IBAction func actionTextb(_ sender: Any) {
print("you writted \(String(describing: amauntEnter.text!))----")
let storyboard = UIStoryboard.init(name: "convert ID", bundle: nil)
let update = storyboard.instantiateViewController(withIdentifier: "convert ID") as! Convert_Table_Third_Cell
update.amauntOut.text = "hola"
let aa = "hola hillel----------------------"
print(aa)
print(theNumber.usedParameters(ArrayOfNumbers: unitInOutlet, TipOfData: 3))
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
print("this is the valeu \(theNumber.hola)")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
this is the second where is the label that I want to edit
import UIKit
class Convert_Table_Third_Cell: UITableViewCell {
#IBOutlet var amauntOut: UILabel!
#IBOutlet var UnityMeasurment: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Your approach is incorrect. A view controller is initiated when it is displayed on the screen. One and only on view controller object can be displayed at one time. In your code, you are initiating a brand new view controller and set text to outlets. So that won't work. Instead, you need to set text to the text field on the existing instance of you view controller.
To do so, in the view controller that you want to receive text field content updates, register in notification center to receive a content update function calls.
NotificationCenter.default.addObserver(self, selector: #selector(listnerFunction(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)
func listnerFunction(_ notification: NSNotification) {
if let data = notification.userInfo?["data"] as? String {
self.textField.text = data
}
}
Then in another view controller, if you want to send text to the above view controller and update text, simply post the data to notification center
let data:[String: String] = ["data": "YourData"]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: data)
Instance member 'instantiateViewController' cannot be used on type 'UIStoryboard'; did you mean to use a value of this type instead?
It say that you cannot use instance member of instantiateViewController by a class of UIStoryboard.
Change var update: Convert_Table_Third_Cell = UIStoryboard.instantiateViewController(UIStoryboard) as! Convert_Table_Third_Cell
to var update: Convert_Table_Third_Cell = storyboard?.instantiateViewController(withIdentifier: {YourStoryBoardID}) as! Convert_Table_Third_Cell

change label from another viewcontroller on swift

I want to change label from another viewController.
First viewcontroller is MenuController. Second one is LoginViewController.
I want to change MenuController's Label.text from LoginViewController.
In LoginViewController:
let viewController = MenuController()
viewController.changeLabel("logout")
In MenuController:
class MenuController: UITableViewController {
var attractionImages = [String]()
var attractionNames = [String]()
var webAddresses = [String]()
#IBOutlet weak var loginLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
loginLabel.text = "Login"
print(loginLabel.text)
}
func changeLabel(Log: String)O {
self.loginLabel.text = log
print (log)
}
But an error occur.
fatal error: unexpectedly found nil while unwrapping an Optional value
How can I solve it?
Thanks for your help.
Another way to achieve that is you can use NSNotificationCenter. Blow is the example for that:
In your MenuController add this code:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "refreshLbl:", name: "refresh", object: nil)
}
Also add this helper method:
func refreshLbl(notification: NSNotification) {
print("Received Notification")
lbl.text = "LogOut"
}
Now in your LoginViewController your back button action will look like:
#IBAction func back(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("refresh", object: nil, userInfo: nil)
self.dismissViewControllerAnimated(true, completion: nil)
}
Now when ever you press back button from LoginViewController your refreshLbl method will call from MenuController.
For more info refer THIS example.
Swift 3 version:
In your MenuController (where the label needs to be changed) add this code:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(refreshLbl),
name: NSNotification.Name(rawValue: "refresh"),
object: nil)
}
Also add this helper method:
#objc func refreshLbl() {
print("Received Notification")
lbl.text = "LogOut"
}
Now in your LoginViewController your back button action will look like:
#IBAction func backButton(_ sender: Any) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "refresh"), object: nil)
// Any additional code...
}
Now when ever you press back button from LoginViewController your refreshLbl() method will call from MenuController.

Resources