Swift: UICollectionView + Custom Segue makes problems - swift

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
}

Related

Make a segue to a tableview controller from a view controller

I am trying to make a segue to a table view controller when a button is tapped in my view controller programmatically. Here is my code:
#objc func editProfileButtonAction(sender: UIButton!) {
print("ButtonTapped")
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "EditProfile" {
var editProfileTableViewController = segue.destination as! EditProfileTableViewController
editProfileTableViewController = self
}
}
}
}
I really could use some help. I also need to make a segue to a collection view controller using a button in the same view controller.
Okay to clarify that. There is no way to create a segue programmatically. Segues are the arrows on storyboard linking from one to another VC. They are called with: performSegue. This calls the function prepare.
If you want to show a new VC when hitting a button (without segue), then you use the present(VC(), animated: true, completion: nil) } inside the button function. The VC is presented modally.
#objc func editProfileButtonAction(sender: UIButton!) {
print("editProfileButtonAction")
present(EditProfileTableViewController(), animated: true, completion: nil)
}
Make sure, that the segue in the Storyboard has exactly the identifier: "EditProfile". Normally I'm writing identifiers with lower letter in the beginning. You also need to prepare for Segue. For example set the delegate:
// Set ViewController class as the delegate of the EditProfileTableViewControllerDelegate protocol
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "EditProfile" {
let editProfileTableViewController = segue.destination as! EditProfileTableViewController
editProfileTableViewController = self
}
}
}
At one point in my coding time, I deleted all my storyboard because of too many error's that I could hardly solve. Now I'm doing it all programmatically. At first it was a bit hard to set up all the view's by myself but after all I'm very glad I'm not using storyboards anymore. For some stuff I need xib's, and for testing storyboard. Just if you're interested: the most iOS programmers are using storyboard, so it's okay if you go on with that.
The advantage of doing it all programmatically is that there are no segue's anymore. So just present, and on navigation VC's push, pop, ...

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 3 popover with Xcode

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?

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"
}
}
}