swift 3 popover with Xcode - swift

i have an nstableview with custom cells.
each cell has a nsbutton.
now i would like to create an popover to another view controller, when the button was pressed.
what i have done?
i create a segue connection between my nstableview and the popover view controller and give it an identifier.
in my custom cell i created i IBOutle action for the nsbutton:
#IBAction func actionButtont(_ sender: Any) {
let vcTable = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "vcTable ") as! TableView
vcTable.startSegue(text: "Test")
}
and in my TableViewController:
var text = String()
func startSegue(text:String) {
text = text
self.performSegue(withIdentifier: "Popover", sender: self)
}
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
let vc = segue.destinationController as! Popover
vc.text = text
}
My "text String" will passed successfully.
but me segue will not work, because my app crashes before with this error:
[General] -[NSPopover showRelativeToRect:ofView:preferredEdge:]: view has no window. You must supply a view in a window.
what did I wrong?

Related

Prepare For Segue on return

So the code I am trying to implement in Swift is based upon this answer here for passing data back from a ViewController:
Passing Data with a Callback
Now my issue is after I have called:
self.navigationController?.popViewController(animated: true)
The Prepare For Segue function isn't called in my original View Controller. I assume it shouldn't be called anyway but from that answer I assume there is a possible way to do so?
First View Controller Snippets
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//ignore this segue identifier here, this function works when I am showing a new VC
if(segue.identifier == "certSegue"){
let certVC = segue.destination as! CertificateViewController
certVC.profileModel = profileModel
}
//this is what I need to be called
if(segue.identifier == "dpSegue"){
print("dpSegue")
let dpVC = segue.destination as! DatePickerViewController
dpVC.callback = { result in
print(result)
print("Data")
// do something with the result
}
//dpVC.dailyBudgetPassedThrough = "Test"
}
}
func showDatePicker(){
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "DatePickerVC") as? DatePickerViewController
self.navigationController?.pushViewController(vc!, animated: true)
}
Second View Controller
import UIKit
class DatePickerViewController: UIViewController {
var callback : ((String)->())?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func sendBackUpdate(){
print("Callback")
callback?("Test")
}
#IBAction func cancelButton(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
}
#IBAction func updateButton(_ sender: Any) {
sendBackUpdate()
self.navigationController?.popViewController(animated: true)
}
}
prepareForSegue is called if in Interface Builder a segue is connected
from a table/collection view cell to a destination view controller and the cell is tapped.
from a source view controller to a destination view controller and performSegue(withIdentifier:sender:) is called in the source view controller.
It's not called when a view controller is going to be presented with pushViewController
In your case assign the callback after instantiating the controller in showDatePicker, prepare(for segue is not needed.
func showDatePicker(){
let vc = UIStoryboard(name: "Main", bundle: .main).instantiateViewController(withIdentifier: "DatePickerVC") as! DatePickerViewController
vc.callback = { result in
print(result)
print("Data")
// do something with the result
}
self.navigationController?.pushViewController(vc, animated: true)
}

Value not passing between viewControllers

Struggling to get my viewControllers to send value from the main viewController to a second. I want it to happen on a button click, I'm going to get the value from the button and pass it to the new form. But it just isn't working.
Code for main ViewController
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func butClick(_ sender: UIButton) {
NSLog("Button Pressed : %#",[sender .currentTitle])
//var tt = [sender .currentTitle]
// Create the view controller
let vc = TimesTablesViewController(nibName: "TimesTablesViewController", bundle: nil)
vc.passedValue = "xx"
self.performSegue(withIdentifier: "pushSegue", sender: nil)
}
}
Code for second viewController called TimesTablesViewController:
class TimesTablesViewController: UIViewController {
#IBOutlet weak var titleLabel: UILabel!
var passedValue:String = ""
override func viewDidLoad() {
super.viewDidLoad()
titleLabel?.text = "\(passedValue) Times Table"
}
}
I've followed tutorials but can't seem to solve the problem! Thanks for any help!
Replace
self.performSegue(withIdentifier: "pushSegue", sender: nil)
with
self.present(vc,animated:true,completion:nil)
or ( if the current vc is inside a naigation )
self.navigationController?.pushViewController(vc,animated:true)
Using
self.performSegue(withIdentifier: "pushSegue", sender: nil)
is fit with storyboards not xibs and if this your case then you need to use the above line only inside the button action with implementing this method inside the source vc
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pushSegue" {
if let nextViewController = segue.destination as? TimesTablesViewController{
nextViewController.passedValue = "xx"
}
}
}
I’m assuming that the new view controller is appearing, but you’re simply not seeing the data. If so, you’re evidently using storyboards. The TimesTablesViewController(nibName:bundle:) only works if you’re using XIB/NIBs and manually presenting new view controller.
If you’re really using storyboards, simplify your butClick method:
#IBAction func butClick(_ sender: UIButton) {
NSLog("Button Pressed")
performSegue(withIdentifier: "pushSegue", sender: self)
}
But implement prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? TimesTablesViewController {
destination.passedValue = "xx"
}
}
Assuming the above fixes your problem, I might suggest a further simplification. Notably, if your butClick(_:) method is really only calling performSegue, you can segue to this next scene without any #IBAction method at all:
remove butClick(_:) entirely;
remove the connection between the button and the butClick method in IB, on the “Connections Inspector” tab in the right panel; and
control-drag from the button previously hooked up to butClick(_:) to the scene for TimesTablesViewController.
That will simplify your code further.

Swift: UICollectionView + Custom Segue makes problems

In the following application, I am using an UICollectionView. The text of each cell (string) is downloaded from my SQL-Server. This works pretty well. But somehow, my app sometimes looks as it should, but sometimes not (this happens in simulator, as well as on a native device).
I guess it has something to do with the custom segue I am using, when clicking the settings button (down-right).
#IBAction func settingsButton(_ sender: UIButton) {
// in order to prevent an exception, you have to add an Identity in Main.storyboard
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
// instantiate view controller by storyboard
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "SettingsViewController") as! SettingsViewController
// custom segue
self.performSegue(withIdentifier: "SegueToRight", sender: sender)
// pass email
nextViewController.email = self.email
// switch view controller
self.present(nextViewController, animated:true, completion:nil)
}
Displayed right:
Displayed wrong:
Your problem is probably caused because you are using two different presentation methods, a segue and a present.
Try to remove self.present(nextViewController, animated:true, completion:nil) since the performSegue function will handle the transition for you already.
Also, nextViewController.email = self.email should be set before performSegue to ensure that the data will be passed to the next controller properly.
UPDATE FOR COMMENTS
You need to split your code in two. Inside the #IBAction invoke the performSegue call:
#IBAction func settingsButton(_ sender: UIButton) {
// custom segue
self.performSegue(withIdentifier: "SegueToRight", sender: sender)
}
Then override prepareForSegue:sender::
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextViewController = segue.destination as! SettingsViewController
nextViewController.email = self.email
}

How to programmatically seque to multiple view controllers from a button

I have a view controller with a lot (36) of buttons, and I want each of these buttons to segue to a particular view controller based on a variable that was set earlier in the program. In other words, any button could potentially go to 15 different view controllers based on a variable that was sent to the viewcontroller containing the buttons...
I think I can make this work if I click and drag each button to every viewcontroller... but it seems silly and messy.
I tried doing something like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if variable == "Whatever" {
let send = segue.destination as! AViewController
send.variablesent = (sender as! UIButton).title(for: .normal)!}
}
But this only works if I click and drag the button in the storyboard to the "AViewController".
Any help is appreciated, thanks!!
For that you can make segue from SourceViewController to DestinationViewController instead of from Button to Controller, After that when you call perfromSegue in your button action then pass button reference as sender in perfromSegue call.
#IBAction func buttonAction(_ sender: UIButton) {
self.performSegue(withIdentifier: "segueIdentifier", sender: sender)
}
Now in prepareForSegue cast sender to UIButton and set title according to it.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if variable == "Whatever" {
let send = segue.destination as! AViewController
if let button = sender as? UIButton {
send.variablesent = button.titleLabel?.text ?? "Default value"
}
}
}

Why two successive page opens with segue

when I am selected button twice will open a new page.
//////
Main viewController
var Country = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func taxiAction(sender: AnyObject) {
let opt = ["1","2","3","4","5"]
Country = opt
performSegueWithIdentifier("viewPlaceSegu", sender: sender)
}
...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// get a reference to the second view controller
if segue.identifier == "viewPlaceSegu" {
if let secondViewController = segue.destinationViewController as? TableViewPlace {
// set a variable in the second view controller with the String to pass
secondViewController.tnt = Country as! [String]
}
}
}
////
http://i.stack.imgur.com/LKrN7.jpg
I tried so but didn't realize problem .
Does anyone know about this? :)
A likely cause is that you have connected the segue from the button to the new ViewController in the storyboard. When the button is pressed it will load the segue created in storyboard as well as the one created programatically.
If this is the cause then you would just need to delete the storyboard segue and create a new one from the ViewController rather than from the button.