Pass data with segue form UITabBarItem not working - xcode

I want to pass data from one UIView to another UIView in order to go to the web site which will be different according to UITabBarItem that a user chooses. How can I do it?
func pathButton(dcPathButton: DCPathButton!, clickItemButtonAtIndex itemButtonIndex: UInt) {
switch itemButtonIndex {
case 0:
// working but can’t send data with it
self.performSegueWithIdentifier("goto", sender: self)
// not working as function is not trigger
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "goto" {
let vc = segue.destinationViewController as! WebMainViewController
vc.goURL = "http://google.com“
dcPathButton.delegate = self
println(“go to http//google.com”)
}
case 1:
println(“go to http://apple.com”)
case 2:
println(“go to http://imbd.com”)
case 3:
println(“go to http://facebook.com”)
default:
println(“go to http://cnn.com”)
}

Have you tried
func pathButton(dcPathButton: DCPathButton!, clickItemButtonAtIndex itemButtonIndex: UInt) {
switch itemButtonIndex {
case 0:
self.performSegueWithIdentifier("goto", sender: self)
// plus other cases
}
and then in a different section of your class you implement:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "goto" {
let vc = segue.destinationViewController as! WebMainViewController
vc.goURL = "http://google.com“
}
Obviously make sure that your segue is hooked up and has "goto" set as an identifier.
Perhaps you could have some logic in prepareForSegue that sets vc.goURL instead of the switch statement that you currently have? For example, the user selects a particular value, you save that value to a variable and then when they click on a button to start the segue you check the value of that variable and set vc.goURL accordingly?

Related

Swift 4: Cannot convert return expression of type 'Int' to return type '[String : Any]?'

In a prepare(for segue:) I add the id:Int of the selected project into user defaults like this
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let viewController = segue.destination as? ReceiverVC, let project = sender as? Project {
defaults.set(project.id!, forKey: "currProject")
viewController.project = project
}
}
In my ApiRouter I need to access that value (I think I need it as a string since it is a URL parameter) but am getting
"Cannot convert return expression of type 'Int' to return type
'[String : Any]?'"
with this code
let params: ([String: Any]?) = {
switch self {
case .getAllProjects:
return nil
case .getAllParts:
return nil
case .getProjectParts:
return UserDefaults.standard.integer(forKey: "currProject")
}
}()
I don't know another way to capture the selected row and run back to the api to get the correct records
case .getProjectParts:
return ["your key":UserDefaults.standard.integer(forKey: "currProject")]
}

Confused about NSTableView Identifier on Table Cell View

I am able to create simple view based NSTableViews but there's one point I don't understand about identifiers.
In an NSTableView you typically give a column an identifier and then implement the delegate method:
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
And then you switch on the column to do what you need, something like:
switch tableColumn!.identifier {
case "firstColumn":
//do something...
case "secondColumn":
//do something else...
default:
return nil
}
However additionally you can give each Table Cell View an identifier as well. So in the above example, say I didn't give the identifier to the column, and instead gave the identifier to the Table Cell View itself.
I presumed that then I could do something like this in the delegate method:
if let firstColumnCellView = tableView.make(withIdentifier: "firstColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the first column"
return view
} else if let secondColumnCellView = tableView.make(withIdentifier: "secondColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the second column"
return view
} else {
return nil
}
This works, but never makes it past the first if let statement, and so all my cells say "Hi! I'm in the first column"
More Info:
Something else I don't understand: it seems that the Table Cell View identifier overrides the identifier to the column.
If I go to the document outline and assign identifiers something like this:
tableColumn: "firstColumn"
tableViewCell: "firstColumnCell"
tableColumn: "secondColumn"
tableViewCell: "secondColumnCell"
and then supply both the column identifier and the cell identifier, it works!
switch tableColumn!.identifier {
case "firstColumn":
if let firstColumnCellView = tableView.make(withIdentifier: "firstColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the first column"
return view
} else {
return nil
}
case "secondColumn":
if let secondColumnCellView = tableView.make(withIdentifier: "secondColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the second column"
return view
} else {
return nil
}
default:
return nil
}
But it crashes if I allow the switch statement to ignore the cell identifier for the second column, and fall through to trying to use the column identifier.
switch tableColumn!.identifier {
case "firstColumn":
if let firstColumnCellView = tableView.make(withIdentifier: "firstColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the first column"
return view
} else {
return nil
}
default:
break
}
let cellView = tableView.make(withIdentifier: tableColumn!.identifier, owner: self) as! NSTableCellView
cellView.textField?.stringValue = "hello"
return cellView
//CRASH: Unexpectedly found nil when unwrapping tableColumn!.identifier
// The column both exists and has an identifier of "secondColumn", so how could
//this be nil?
And it seems I can confirm this overriding behavior by renaming the secondColumnCell to the same name as the secondColumn:
tableColumn: "firstColumn"
tableViewCell: "firstColumnCell"
tableColumn: "secondColumn" <--- Same Name
tableViewCell: "secondColumn" <-- Same Name
And now the code runs as expected and doesn't crash.
If I read your last chunk of code correctly, you instantiate (or retrieve from a used-views-no-longer-on-screen pool - see here) a view with the identifier firstColumnCell.
As long as the identifier is valid (you have somewhere a nib defining the view) the method will always return a non-nil view so the first if let ... will always succeed.
So the view.textField?.stringValue = "Hi! I'm in the first column" will execute thus showing the message in the cell and then it will return the view to be used by the NSTableView and exit your method.
The next if let ... statements will never have a chance to execute.

Why am I receiving these errors when trying to pass a variable on a segue in swift?

I am trying to build upon answer which I was given here. What I am trying to is very simple - I want a text field which you can enter text into. You press the go button and it takes you to a new view and replaces the text on a label on that page with whatever the user entered in the box. The is the code I am using on the first page.
import UIKit
class ViewController: UIViewController {
#IBOutlet var entry: UITextField!
let dictionary = entry.text // Line 7 ERROR
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "viewTwo"
{
if let destinationVC = segue.destinationViewController as? viewTwo{
destinationVC.dictionary = self.dictionary // Line 24 ERROR
}
}
}
#IBAction func goToViewTwo(sender: AnyObject) {
performSegueWithIdentifier("viewTwo", sender: self)
}
}
I am only including the code from the first view because i know the code from the second view is working.
I didn't encounter an error until I tried to use the text field - before when I just had a pre-choses text to transfer over it worked. Before, instead of having let dictionary = entry.text I had let dictionary = "foo" and it worked.
So my question is exactly the same thing but have a text field instead of pre-chosen text - what I really want to know is why my code didn't work before.
The errors I got were on line 7 (I have labeled the lines above which had the errors) - 'ViewController.Type' does not have member names 'entry' and there was also an error on line 24 but I suspect this is related to this error and will be fixed if this error is also fixed. Just incase though, the error on line 24 was: 'ViewController.Type' does not have member names 'dictionary'
Thank you.
You should set the dictionary to var dictionary = "" in the declaration. You use var instead of let here, so that you can change the value of the dictionary later.
Then inside your #IBAction func goToViewTwo(sender: AnyObject){} method, you set the self.dictionary = entry.text
#IBAction func goToViewTwo(sender: AnyObject) {
dictionary = entry.text
performSegueWithIdentifier("viewTwo", sender: self)
}
Alternatively, you can just do the following inside prepareForSegue() method.
This way, you dont need to declare a dictionary to hold the text value of your UITextField, you can just pass the text value from your entry to the second view controller's dictionary variable.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "viewTwo"
{
if let destinationVC = segue.destinationViewController as? viewTwo{
destinationVC.dictionary = self.entry.text
}
}
}
A dictionary is not constant, so declare it as lazy var, not let:
lazy var dictionary: String {
return entry.text
}()

Swift PrepareForSegue EXC_BREAKPOINT

I have a runtime error: Thread 1: EXC_BreakPoint(cod=EXC_I1386_BPT,subcode=0x0)
I did not set any breakpoint inside Xcode. From the debugger,error is due to PrepareForSegue that stops a thread "swift_dynamicCastClassUnconditional"
BarTableViewController1 Class
#IBAction func solve_PressedBar(sender: AnyObject) {
self.performSegueWithIdentifier("SolveBar", sender: sender)
}
override func prepareForSegue ( segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "SolveBar") {
var svcBar = segue.destinationViewController as BarTableViewController2
for items in textField1 {
length.append((items as NSString).floatValue)
}
for items in textField2 {
youngMod.append((items as NSString).floatValue)
}
for items in textField3 {
diameter.append((items as NSString).floatValue)
}
for items in textField4 {
forceBarWall.append((items as NSString).floatValue)
}
self.funcForceBarWall()
println("variables are from TableViewController2")
println(self.length)
println(self.youngMod)
println(self.forceBarWall)
println(self.diameter)
svcBar.length2 = self.length
svcBar.youngMod2 = self.youngMod
svcBar.diamter2 = self.diameter
svcBar.forceBarWall2 = self.forceBarWall
println("testing from bar viewcontroller1")
}
}
}
I'd look here first:
var svcBar = segue.destinationViewController as BarTableViewController2
Check your storyboard and make sure that the segue does connect to a BarTableViewController2 item. Sometimes when dragging, I connect the segue to something else accidentally.

Passing Data in Swift

I have been looking for an answer for this, but have only found answers for segues.
I have viewController1 with a button that segues to viewController2. There is no code for this, I set it up through Interface builder. On viewController2 I have a button that dismisses itself with
self.dismissViewControllerAnimated(true, completion, nil)
I want to pass a string from viewController2 back to viewController1 when the view is dismissed. How do I go about doing this? Also, I am using swift.
Thanks in advance!
There are two common patterns, both of which eliminate the need for viewController2 to know explicitly about viewController1 (which is great for maintainability):
Create a delegate protocol for your for viewController2 and set viewController1 as the delegate. Whenever you want to send data back to viewController1, have viewController2 send the "delegate" the data
Setup a closure as a property that allows passing the data. viewController1 would implement that closure on viewController2 when displaying viewController2. Whenever viewController2 has data to pass back, it would call the closure. I feel that this method is more "swift" like.
Here is some example code for #2:
class ViewController2 : UIViewController {
var onDataAvailable : ((data: String) -> ())?
func sendData(data: String) {
// Whenever you want to send data back to viewController1, check
// if the closure is implemented and then call it if it is
self.onDataAvailable?(data: data)
}
}
class ViewController1 : UIViewController {
func doSomethingWithData(data: String) {
// Do something with data
}
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
// When preparing for the segue, have viewController1 provide a closure for
// onDataAvailable
if let viewController = segue.destinationViewController as? ViewController2 {
viewController.onDataAvailable = {[weak self]
(data) in
if let weakSelf = self {
weakSelf.doSomethingWithData(data)
}
}
}
}
}
I used the code from the first answer in a transition between controllers WITHOUT prepareForSegue and worked for me as well.
Here's the sample code.
The First View Controller:
#IBAction func dpAgendaClick(sender:UIBarButtonItem) {
///instantiating view controller with identifier
if let datePickerViewController = storyboard?.instantiateViewControllerWithIdentifier("DatePickerViewController")
as? DatePickerViewController {
///bring instantiated view controller to front
self.presentViewController(datePickerViewController, animated: true, completion: nil)
///wrapping the data returned
datePickerViewController.onDataFiltroAvailable = {[weak self]
(dataFiltro) in
if let weakSelf = self {
///use dataFiltro here
}
}
The second View Controller:
var onDataFiltroAvailable: ((dataFiltro: String) -> ())?
///private var
var dataFiltro: String = ""
///the returning data is obtained on the datePickerChanged event
#IBAction func datePickerChanged(sender: UIDatePicker) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
dateFormatter.dateFormat = "yyyy-MM-dd"
dataFiltro = dateFormatter.stringFromDate(datePicker.date)
}
///dismiss the controller on button click
#IBAction func dpOkClick(sender: UIButton) {
///"returning" the data
self.onDataFiltroAvailable?(dataFiltro: dataFiltro)
dismissViewControllerAnimated(true, completion: nil)
}
(Swift 2.1, Xcode 7, iOS9)
If you don't want it to be tightly coupled only between 2 ViewControllers,
You can also use the Notification Design Pattern (Post & Observe), which is mainly used to pass on the same object/information from one VC to multiple View Controllers.
For your scenario :
In VC2.swift :
#IBAction func BackBtn(sender: UIButton) {
NSNotificationCenter.defaultCenter().postNotificationName("ThisIsTheMessage", object: nil, userInfo:["ObjectBeingSent":yourObject])
}
And in VC1.swift :
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("yourFunction:"), name: "ThisIsTheMessage", object: nil)
}
func yourFunction(theNotification : NSNotification) {
if let extractInfo = theNotification.userInfo {
//code to use the object sent from VC2, by extracting the object details
}
}
Common Practise is:
Pass data forward -> Use PrepareForSegue
Pass data backward to the previous View Controller-> Protocol and Delegation
Pass data across multiple View Controllers -> Notifications : Post and Observe(observe in all the View controllers where you are using the object details)

Resources