I'm kinda new to swift and i'm having trouble implementing this segue between "ViewController" and "PopUpViewController." I'm trying to pass over an array of quotes between the view but keep getting the "Receiver has no segue with identifier 'popUpSegue' error."
I made absolutely sure that my segue's identifier was written correctly across my code and storyboard, i've done the "product --> clean" resolution proposed on another post, nothing has worked.
Something to note: I'm not performing this segue using a button, I had manually set it from the ViewController storyboard (ctrl-clicked the yellow button on the view controller). I'm not sure if that might have anything to with my issue.
ViewController.swift
import UIKit
class ViewController: UIViewController {
// MARK: Properties
#IBOutlet weak var toucanTut: UIImageView!
#IBOutlet weak var canTooLabel: UILabel!
let quoteArray: NSMutableArray = NSMutableArray()
let tapRecon = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tapRecon.addTarget(self, action: #selector(ViewController.tappedView))
toucanTut.isUserInteractionEnabled = true;
toucanTut.addGestureRecognizer(tapRecon)
let urlPath: String = "https://web.njit.edu/~mid6/service.php"
//Retrieves data from DB and handles it
if let url = URL(string: urlPath){
if let data = try? Data(contentsOf: url){
let json = try? JSON(data: data)
//print(json!)
parse(json: json!)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: Actions
func tappedView(){
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "quotePopUp") as! PopUpViewController
self.addChildViewController(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParentViewController: self)
}
func parse(json: JSON) {
for(i , object) in json{
let quote = object["quote"].stringValue
let author = object["author"].stringValue
//print(quote)
let data = IncomingData()
data.quote = quote
data.author = author
quoteArray.add(data)
//print(data)
}
self.performSegue(withIdentifier: "popUpSegue", sender: quoteArray)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popUpSegue"
{
if let destinationVC = segue.destination as? PopUpViewController {
destinationVC.newArray = sender as! NSMutableArray
}
}
}
}
PopUpViewController.swift
import UIKit
class PopUpViewController: UIViewController {
//Properties
var feedItems: NSArray = NSArray()
var selectedLocation : IncomingData = IncomingData()
var newArray: NSMutableArray = NSMutableArray()
#IBOutlet weak var quoteText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white.withAlphaComponent(0.8)
checkQuotes()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//Actions
#IBAction func closePopUp(_ sender: AnyObject) {
self.view.removeFromSuperview()
}
// check is quoteArray passed over
func checkQuotes(){
print(newArray.count)
}
}
sender The object that you want to use to initiate the segue.
This object is made available for informational purposes during the
actual segue.
Change these lines
self.performSegue(withIdentifier: "popUpSegue", sender: quoteArray)
destinationVC.newArray = sender as! NSMutableArray
To
self.performSegue(withIdentifier: "popUpSegue", sender: self)
destinationVC.newArray = quoteArray
Related
I am looking to pass data through a text box, and into an array (Second View Controller), so it can be added to a pool of data and randomly chosen.
For instance, you would input your name (and/or another name) into the text field in the First VC and and it would go into an array that would tell them which color they are shown in the Second VC.
Essentially, I'm trying to pass the data to a variable (which will hold all the names inputed), and append the variable to the array, then click a button to generate and randomly pull from the array via arc4random. My problem is its not appending. Any help would be greatly appreciated.
Here is my code:
VC 1:
import UIKit
class ViewController: UIViewController {
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.
}
#IBAction func StartTapped(_ sender: Any) {
performSegue(withIdentifier: "SecondViewControllerSegue", sender: self)
}
#IBOutlet weak var AddPlayerTextField: UITextField!
#IBAction func AddTexttoArrayBtn(_ sender: Any) {
if AddPlayerTextField.text == nil
{
performSegue(withIdentifier: "SecondViewControllerSegue", sender: self)
}
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let SecondViewController = segue.destination as! ViewController2
SecondViewController.myString = AddPlayerTextField.text!
VC 2:
import UIKit
class ViewController2: UIViewController {
var myString = String ()
var TeamBuildingQuestions = [ "is Red", "is Blue", "is Green", "is Purple","is Teal","is Orange", "is Red", "is Grey", "is Pink"]
var MasterTeamBuildingQuestionsArray = [ "Hello", "beep bop bbeep", "Boop", "Bap","Bospop","bob the builder"]
#IBOutlet weak var DisplayArray: UILabel!
#IBAction func NextQuestionBtn(_ sender: Any) {
let RandomArrayNumber = Int (arc4random_uniform(UInt32(TeamBuildingQuestions.count)))
let QuestionDisplayed = TeamBuildingQuestions[RandomArrayNumber]
DisplayArray?.text = QuestionDisplayed
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
TeamBuildingQuestions.append(myString)
}
In VC2, Instead of didReceiveMemoryWarning, implement TeamBuildingQuestions.append(myString) in viewDidLoad.
Here is a bug:
if AddPlayerTextField.text == nil{
}
should be
if AddPlayerTextField.text != nil && AddPlayerTextField.text.count > 0{
}
I try to pass an image from one ViewController1 to another ViewController2 when a button is tapped. I am using segues in my Storyboard.
ViewController1
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var imagePicker: UIImagePickerController!
#IBAction func editPhoto(_ sender: UIButton) {
let editViewController = storyboard?.instantiateViewController(withIdentifier: "EditImageViewController") as! EditImageViewController
editViewController.imageForEdit = imageView.image!
print(imageView)
print(imageView.image!)
navigationController?.pushViewController(editViewController, animated: true)
}
#IBOutlet weak var imageView: UIImageView! {
didSet {
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
}
}
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.
}
}
print(imageView) results in
some(UIImageView: 0x7fc34df0e630; frame = (8 29; 303 443); clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = CALayer: 0x60000023e520)
and print(imageView.image!) in
size {3000, 2002} orientation 0 scale 1.000000
ViewController2
import UIKit
class EditImageViewController: UIViewController {
var imageForEdit = UIImage()
#IBOutlet weak var editingImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
editingImage.image = imageForEdit
print(imageForEdit)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
print(imageForEdit) results in
size {0, 0} orientation 0 scale 1.000000
Why is that? Why isnt the image passed to ViewController2?
It seems to me that this could be a lot simpler. If you're using a Storyboard with a segue, you can easily accomplish this in prepare(for segue: UIStoryboardSegue, sender: Any?). Here's a sample storyboard:
In this case, ViewController has a segue on the Edit Image button to the EditImageViewController.
When you push that button, you get your prepare(for segue: call. Here's a sample ViewController:
class ViewController: UIViewController {
var imagePath = Bundle.main.path(forResource: "icon8", ofType: "png")
var anEditableImage:UIImage?
override func viewDidLoad() {
super.viewDidLoad()
if let imagePath = imagePath {
anEditableImage = UIImage(contentsOfFile: imagePath)
}
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? EditImageViewController {
destination.imageForEdit = anEditableImage
}
}
}
And here's the EditImageViewController:
class EditImageViewController: UIViewController {
var imageForEdit:UIImage?
override func viewDidLoad() {
super.viewDidLoad()
print(imageForEdit ?? "Image was nil")
}
}
Using prepare(for segue: UIStoryboardSegue, sender: Any?) is the preferred way for a Storyboard-based app to pass data between View Controllers. It's very simple and it works.
Sample project is here if you want to see the working code:
https://www.dropbox.com/s/eog01bmha67c5mv/PushImage.zip?dl=0
var imageForEdit: UIImage? {
didSet {
editingImage.image = imageForEdit
}
}
Replace var imageForEdit = UIImage() with var imageForEdit : UIImage?
i test code my self , make sure that imageView have image
#IBOutlet weak var imageView: UIImageView! {
didSet {
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
}
}
You can create an public variable so you can pase data from one viewcontroller to another view controller.
class variables
{
static var image = UIImage()
}
the 2 controller:
#IBOutlet weak var anotherviewcontroller: UIImageView!
anotherviewcontroller.image = variables.image
I have three data view controllers: settingsVC.swift, menuVC.swift and GameScene.swift
I am attempting to use a prepareforsegue method for the transfer of [UIColor] and [String] array's from settings to menuVC and then another transfer from menuVC to GameScene.swift using a instantiateViewController. I have declared empty arrays in each and just want to pass data from one page to another and am receiving 'use of unresolved identifier' on both of my arrays. Any help is appreciated.
class MenuVC: UIViewController {
var colourSettingsMenu = [UIColor]()
var otherSettingsMenu = [String]()
//action function for button to transfer to a 1 player game
#IBAction func Player1Game(_ sender: MenuVC) {
moveToGame(game: .player1, colourSettings: colourSettingsMenu, otherSettings: otherSettingsMenu)
}
//action function for button to transfer to a 2 player game
#IBAction func Player2Game(_ sender: MenuVC) {
moveToGame(game: .player2, colourSettings: colourSettingsMenu, otherSettings: otherSettingsMenu)
}
func moveToGame(game : gameType, colourSettings: [UIColor], otherSettings: [String]) {
let GameVC = self.storyboard?.instantiateViewController(withIdentifier: "GameVC") as! GameViewController
currentGameType = game
colourSettingsGame.GameVC = colourSettings;
otherSettingsGame.GameVC = otherSettings;
self.navigationController?.pushViewController(GameVC, animated: true)
}
#IBAction func moveToSettings(_ sender: Any) {
performSegue(withIdentifier: "settings", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender:Any?) {
if (segue.identifier == "settings") {
let SettingsVC = segue.destination as! SettingsVC
SettingsVC.colourSettings = colourSettingsMenu
SettingsVC.otherSettings = otherSettingsMenu
}
}
}
class SettingsVC : UIViewController {
//Colours for segue transfer
var player1Colour: UIColor?
var player2Colour: UIColor?
var ballColour: UIColor?
// other settings to allow segue transfer
// do we need these
var AIDiff: String?
var paddleSi: String?
var ballSi: String?
//arrays for segue transfer
var colourSettings = [UIColor]()
var otherSettings = [String]()
override func prepare(for segue: UIStoryboardSegue, sender:Any?) {
let colourSettings = [player1Colour, player2Colour, ballColour]
let otherSettings = [AIDiff, paddleSi, ballSi]
if (segue.identifier == "back") {
let MenuVC = segue.destination as! MenuVC
MenuVC.colourSettingsMenu = colourSettings as! [UIColor]
MenuVC.otherSettingsMenu = otherSettings as! [String]
}
}
class GameScene: SKScene {
// init colour settings array
var colourSettingsGame = [UIColor]()
// init other settings array
var otherSettingsGame = [String]()
var currentGameType = gameType?.self
}
You should not name a variable like a class name , here
override func prepare(for segue: UIStoryboardSegue, sender:Any?) {
if (segue.identifier == "settings") {
let nextSettings = segue.destination as! SettingsVC
nextSettings.colourSettings = colourSettingsMenu
nextSettings.otherSettings = otherSettingsMenu
}
}
and here
override func prepare(for segue: UIStoryboardSegue, sender:Any?) {
let colourSettings = [player1Colour, player2Colour, ballColour]
let otherSettings = [AIDiff, paddleSi, ballSi]
if (segue.identifier == "back") {
let nexrMenu = segue.destination as! MenuVC
nexrMenu.colourSettingsMenu = colourSettings as! [UIColor]
nexrMenu.otherSettingsMenu = otherSettings as! [String]
}
So I have the following layout for my iOS app.
What I'm intending to do is put a table view in the purpleVC to control the Green viewcontroller...the top peachVC will have text in it which will need to change. I'm just not sure how to control one view controller from another. This includes having the purple slide in and out when a button on the GreenVC is clicked. I know there are classes out there to do this however I want to learn as well.
TESTING DELEGATES:
MAINVIEW CONTROLER
import UIKit
protocol Purpleprotocol {
func buttonpressed()
}
protocol Greenprotocol {
}
extension UIViewController {
func alert(message: String, title: String = "") {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
}
class MainViewController: UIViewController,Purpleprotocol,Greenprotocol {
weak var infoNav : UINavigationController?
weak var greenVC: GreenVC?
weak var purpleVC: PurpleVC?
weak var peachVC: PeachVC?
func buttonpressed() {
alert(message: "This is message")
print("buttonpressed")
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minutes = calendar.component(.minute, from: date)
greenVC?.greenlabel.text = String(hour) + ":" + String(minutes)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "contentSegue" {
let infoNav = segue.destination as! UINavigationController
}
}
}
PURPLEVIEW CONTROLER
class PurpleVC: UIViewController {
var delegate: Purpleprotocol?
#IBAction func butclick(_ sender: UIButton) {
alert(message: "infunction")
delegate?.buttonpressed()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Thanks
R
It does depend on the case but to see a few examples:
A) You can connect it through the delegates. Your main view controller has 3 child view controllers to which it should report changes. It should also assign itself as a delegate to all 3 child controllers where it will get all notifications for events. This would look like
func purpleViewController(sender: PVC, selectedItem: Item) {
self.greenViewController.showItem(item: selectedItem)
self.peachVC.showItem(item: selectedItem)
}
func purpleViewController(sender: PVC, shouldSetMenuClosed closed: Bool) {
self.menuConstraint.constant = closed ? targetWidth : 0.0
}
B) You may have a data model which controls the whole screen and has a delegate for each of the children. The model will report any changes to its delegates so they may react accordingly. Main view controller would create an instance of this model when it loads and pass it to all of the children view controllers. The children would then directly manipulate the model:
In green controller:
func onTap() {
mode.menuShown = !mode.menuShown
}
In model:
var menuShown: Bool = true {
didSet {
self.menuDelegate.model(self, changedMenuShownStateTo: menuShown)
}
}
In main view controller:
func model(_ sender: Model, changedMenuShownStateTo shown:Bool) {
self.menuConstraint.constant = shown ? 0.0 : targetWidth
}
C) You can use notifications where any of the controllers may post to notification center a custom notification and other controllers may observe the notifications and act accordingly.
There are many other ways in doing so but these probably most popular. See if any of them fits you...
Delegation.
Your MainViewController will become a delegate to each of the embedded VC's that want to pass back information. From your description you'll need two delegate relationships:
protocol PurpleProtocol {
func selected(row: Int, text: String)
}
protocol GreenProtocol {
func slideButtonPressed()
}
Have MainViewController implement these protocols. Give identifiers to the embed segues. You can find them in the Document Outline view. In prepareForSegue, retain pointers to the embedded VC's and pass your self as the delegate:
class MainViewController: UIViewController, PurpleProtocol, GreenProtocol {
weak var greenVC: GreenViewController?
weak var purpleVC: PurpleViewController?
weak var peachVC: PeachViewController?
func selectedRow(row: Int, text: String) {
// do something useful
}
func slideButtonPressed() {
// slide purple view in or out depending on current state
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EmbedPurple" {
let dvc = segue.destination as! PurpleViewController
purpleVC = dvc
dvc.delegate = self
}
else if segue.identifier = "EmbedGreen" {
let nav = segue.destination as! UINavigationController
let dvc = nav.topViewController as! GreenViewController
greenVC = dvc
dvc.delegate = self
} else if segue.identifier = "EmbedPeach" {
peachVC = segue.destination as! PeachViewController
}
}
}
In your embedded VC's, add a delegate pointer and call the delegate with the protocol method when it is time:
class GreenViewController: UIViewController {
weak var delegate: GreenProtocol?
#IBAction slideButtonPressed(sender: UIButton) {
delegate?.slideButtonPressed()
}
}
class PurpleViewController: UITableViewController {
weak var delegate: PurpleProtocol?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.selected(row: indexPath.row, text: modelArray[indexPath.row])
}
}
my code works fine in choosing the picture from the gallery and display it in the same view,
what am stuck into now, is transferring that UIImageView chosen to the next activity when "next" button is clicked
here is the code that opens the gallery
#IBAction func gallery(sender: AnyObject) {
if UIImagePickerController.availableMediaTypesForSourceType(.PhotoLibrary) != nil {
picker.allowsEditing = false
picker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
presentViewController(picker, animated: true, completion: nil)
}
}
this is the code that displays the image in the same view
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
var chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
imageChosen.contentMode = .ScaleAspectFit
imageChosen.image = chosenImage
dismissViewControllerAnimated(true, completion: nil)
}
now after the UIImageView saved in the imageChosen, here is the code am using to pass that image to the next view
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var pass:postView = segue.destinationViewController as! postView
if(segue.identifier == "next"){
pass.imgv.image = imageChosen.image
}
}
this line of code that causes the program to crash
pass.imgv.image = imageChosen.image
the imgv in the second view is declared like this
#IBOutlet weak var imgv: UIImageView!
what am i doing wrong here, please direct me
You cannot set data in a view which is not rendered yet . So pass image to second view and set that Image to ImageView in secondView's viewDidLoad
override func prepareForSegue(segue: (UIStoryboardSegue!), sender: AnyObject!) {
if segue.identifier == "next" {
var pass:second = segue.destinationViewController as! second
pass.currentImage=myImageView.image;
}
}
//Second view
class second: UIViewController {
#IBOutlet weak var tempImgView: UIImageView!
var currentImage:UIImage!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if((currentImage) != nil){
tempImgView.image=currentImage;
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You are using weak reference for image view and trying to get it in other controllers . do as following .
#IBOutlet Strong var imgv: UIImageView
Secondly your way of fetching view controller wrong ... Try it as following
if segue.identifier == "ShowCounterSegue"
{
if let destinationVC = segue.destinationViewController as? OtherViewController{
destinationVC.numberToDisplay = counter
}
}
You can't access the UIImageView directly in second View Controller. Instead create variable UIImage in second View Controller and assign your selected UIImage to it. Later in viewdidload of second View Controller set the UIImageView.
First View
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let pass:postView = segue.destinationViewController as! postView
if(segue.identifier == "next"){
pass.tempImage = imageChosen.image
}
}
Second View
#IBOutlet weak var imgv: UIImageView!
var tempImage:UIImage!
override func viewDidLoad() {
super.viewDidLoad()
self.imgv.image=tempImage
}
In the PostView, just create variable like var img: UIImage!
Replace this line pass.imgv.image = imageChosen.image with pass.img = imageChosen.image
In viewDidLoad() of PostView, add this line, imgv.image = image
We can not set properties of IBOutlets of secondView from the firstView, because IBOutlets of ViewController will get memory after execution of viewDidLoad()
So you have to create appropriate data variable to pass data to IBOutlet.
Here is the example code.
class FirstViewController: UIViewController {
#IBOutlet weak var imageViewInput: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//check the condition for your segue.identifier, if any
var destination : NewViewContrller! = segue.destinationViewController as NewViewContrller ;
if let image = imageViewInput?.image {
destination.outputImage = image;
}
}
}
class NewViewContrller: UIViewController {
#IBOutlet weak var outputImageView: UIImageView!
var outputImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if let image = outputImage {
outputImageView.image = image;
}
}
}