call uiactivityviewcontroller with spritekit/skscene - swift

I'm trying to add a share button to social media from within my game to share a highscore. I can't seem to figure it out, and from various other answers, I've arrived at this code (which throws a NSInternalInconsistencyException). Any ideas?
let savedScore = UserDefaults.standard.value(forKey: "HighestScore") as! Int
let textToShare = "My highscore on Panda Pong is \(savedScore)! Can you beat that?"
let objectsToShare = [textToShare]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.excludedActivityTypes = [UIActivityType.airDrop, UIActivityType.addToReadingList]
let vc = UIViewController(nibName: "testview", bundle: nil) as UIViewController
vc.present(activityVC, animated:true, completion:nil)

The correct code is:
let vc = self.view!.window!.rootViewController!
vc.present(activityVC, animated:true, completion: nil)
Not exactly sure why, but messed around with some syntax and arrived at this answer.

You can do it this way in spritekit
...
let rootViewController = view?.window?.rootViewController
rootViewController?.present(activityVC, animated:true, completion:nil)
Instead of trying to instantiate a new ViewController just use the rootViewController (GameViewController).
I would also reccomend to not do things such as
let savedScore = UserDefaults.standard.value(forKey: "HighestScore") as! Int
with force casting the user defaults value (as! Int). You will crash if there is no value yet so change it to this
let savedScore = UserDefaults.standard.value(forKey: "HighestScore") as? Int ?? 0
You now safely check if the UserDefaults value exists and is an Int (as? Int) and if not it will create a new default value (?? 0).
Furthermore try to put your UserDefaults String keys ("HighestScore") into a constant property so you avoid typos.
enum UserDefaultsKey: String {
case highscore = "HighestScore"
}
and than use it like so
UserDefaults.standard.value(forKey: UserDefaultsKey.highscore.rawValue)
Hope this helps

Related

Going from one storyboard to another from a tableView in Swift 5 / Cocoa

have search on that topic without finding a solution that work.
I am building a accounting application with several storyboard. Main, Customer( clients), invoice (factures)... etc. I can go from the main storyboard to the customer of Invoice storyboard by click a button no problem... The button (main SB) is linked to the Customer or Invoice storyboard reference.
In the clients storyboard, I have a tableView with that list the purchased historic of that customer. I would like to to be able to double clic on a specific invoice, and open that invoice in the Invoice storyboard.
The double clic part work fine, print message work... but the program crash after with the message: Could not cast value of type '__NSCFBoolean' (0x7fffaab000c8) to '__C.NSViewControllerPresentationAnimator'
That code was taken andadapted from another post. I have tried different variation withou success ie same error message.
I have not work on the part where I transfer the Invoice number from the client SB to the Invoice SB. I will likely transfer the Invoice number with a segue and have the Invoices program look if that variable if not nil, after loading
Invoice storyboard filename : factures.storyboard
facture ViewController Class : FacturesVC
ViewController storyboardID : facturesVC_id
#objc func tableViewDoubleClick(_ sender:AnyObject) {
if tableView.selectedRow >= 0 {
print ("VC545:", tableView.selectedRow)
//let storyboard = NSStoryboard(name: "factures", bundle: nil)
//let VC = storyboard.instantiateViewController(withIdentifier: "facturesVC_id") // give same error
let VC = NSStoryboard(name: "factures", bundle: nil).instantiateController(withIdentifier: "facturesVC_id") as! FacturesVC
self.present(VC as NSViewController, animator: true as! NSViewControllerPresentationAnimator)
}
}
Your code does not make sense.
It looks like you are trying to call present(_:animator:). If you call that, you need to pass it an animator (an object of type NSViewControllerPresentationAnimator.)
Your code does not create a NSViewControllerPresentationAnimator.
Here is an outline of how you need to change it:
let vc = NSStoryboard(name: "factures", bundle: nil).instantiateController(withIdentifier: "facturesVC_id") as! FacturesVC
let animator = // Code to create an NSViewControllerPresentationAnimator
self.present(vc, animator: animator)
I haven't worked with NSViewControllerPresentationAnimators before. (I mostly work with iOS these days.) You should probably search for tutorials on NSViewControllerPresentationAnimator if you are unsure how to proceed.
Finally, I have found the answer I was looking for...
Here is the code.
#objc func tableViewDoubleClick(_ sender:AnyObject) {
if tableView.selectedRow >= 0 {
let srow = tableView.selectedRow
//print ("VC551:", srow)
fact_nb = Int(fact_tbv[srow].id_f) ?? 0 // invoice nb that you want to segue
let storyboard = NSStoryboard(name: "factures", bundle: nil)
let VC = storyboard.instantiateController(withIdentifier: "facturesVC_id")
//self.presentAsSheet(VC as! NSViewController) work fine for sheet
// self.presentingViewController // data are laoded but nothing show up
// self.presentAsModalWindow(VC as! NSViewController) // OK for modal, cannot be resize , yellow button missing on bar
// self.present(VC as! NSViewController, animator: false as! NSViewControllerPresentationAnimator) // true or false... need a animator
let window = NSWindow(contentViewController: VC as! NSViewController)
window.center()
let windowController = NSWindowController(window: window)
windowController.showWindow(nil)
//see How to Perform Segue https://www.youtube.com/watch?v=JL0xuZ4TXrM
self.performSegue(withIdentifier: "gotofact", sender: nil) // segue identifier name : gotofact
}
}
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
let sb = segue.destinationController as! FacturesVC
print ("VC569:", fact_nb)
sb.factnb = fact_nb
}

Passing value while instantiating the view

Need to adding a value from one view controller to another view controller via the following way. I successfully able to move to the another page but not able to passing the value
sideMenuController?.cache(viewControllerGenerator: {
self.storyboard?.instantiateViewController(withIdentifier: "projectDetailController")
}, with: "333")
sideMenuController?.delegate = self as? SideMenuControllerDelegate
sideMenuController?.setContentViewController(with: "333", animated: Preferences.shared.enableTransitionAnimation)
sideMenuController?.hideMenu()
Add a property inside ProjectDetailController:
class ProjectDetailController: UIViewController {
var someValue: String?
/* more code */
}
Move the ProjectDetailController creation above and, once created - try to inject the value you want:
let projectDetailController = self.storyboard?.instantiateViewController(withIdentifier: "projectDetailController") as! UINavigationController
let projectDetailViewController = projectDetailController.viewControllers.first as! ProjectDetailViewController
projectDetailViewController.projectId = id
sideMenuController?.cache(viewControllerGenerator: { projectDetailController }, with: "333")
sideMenuController?.delegate = self as? SideMenuControllerDelegate
sideMenuController?.setContentViewController(with: "333", animated:
Preferences.shared.enableTransitionAnimation)
sideMenuController?.hideMenu()

How do I reference a variable (a picker view value) from one view controller in another view controller?

I am trying to take these variables from one of my view controller's swift file:
//global
let choice1Box1 = drivingGear[chooseDrivingGear.selectedRow(inComponent: 0)]
let choice2Box1 = drivingGear[chooseDrivenGear1.selectedRow(inComponent: 0)]
This is whats being done to the variables in the original class:
#IBAction func showResultBox1(_ sender: Any) {
let choice1Box1 = self.drivingGear[self.chooseDrivingGear.selectedRow(inComponent: 0)]
let choice2Box1 = self.drivingGear[self.chooseDrivenGear1.selectedRow(inComponent: 0)]
if let intVal1 = Double(choice1Box1), let intVal2 = Double(choice2Box1) {
result = intVal2 / intVal1
let newLabel = String(result)
resultBox1.setTitle(newLabel, for: .normal)
}
}
Just do like this:
Once you have picked the value from the pickerView. Pass it like this.
For Eg. You need to pass a String to next VC:
In SecondVC: Declare a value at top like this:
var strFromPreviousVC:String = String()
In First VC: Send the Value like this:
let objSecondVC = self.storyboard?.instantiateViewController(withIdentifier: "secondVC") as! SecondVC
objSecondVC.strFromPreviousVC = "your selected String here"
self.navigationController?.pushViewController(objSecondVC, animated: true)
And yes its done, it will be passed to SecondVc and you can use the value like print(strFromPreviousVC) in SecondVc
Hope it helps.

custom Swift function has errors

I have formulated the function below so that I can re-use it in my ios app. However, I am unable to build my app because my function below has errors indicated to me but I cannot see what is wrong with it. The function is intended to move the user to a new page in the ios app. Please can someone advise?
func goToPage(goto storyBoardId: String, ofType typeUIViewController: UIViewController.Type) -> UIViewController {
let storyBoard: UIStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
let newPage = storyBoard.instantiateViewController(withIdentifier: storyBoardId) as! typeUIViewController // Error: use of undeclared type 'typeUIViewController'
self.present(newPage, animated: true, completion: nil)
return newPage
}
Your function expects a return value of type UIViewController and you return nothing. So either return an instance you create (if you need one). Or remove return value.
There's a slightly modified variation of your function with generics, which does just what you want. The #discardableResult word before the function tells the compiler that the result can be omitted.
#discardableResult
func goToPage<T>(goto storyBoardId: String,
ofType typeUIViewController: T.Type) -> T
where T: UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let newPage = storyboard.instantiateViewController(withIdentifier: storyBoardId) as! T
self.present(newPage, animated: true, completion: nil)
return newPage
}
USAGE
// Ignore return value
goToPage(goto: "Page", ofType: ViewController.self)
// Preserve return value:
// Thanks to generics, page and page2 types are inferred by the compiler
// page is CustomController and page2 is LoginController
// and you can access corresponding interface:
var page = goToPage(goto: "Page", ofType: CustomController.self)
var page2 = goToPage(goto: "Page", ofType: LoginController.self)
Update
I see that Honey suggests the right idea, but the problem with typecasting still persists. The reason of that is that the compiler has no idea what kind of type typeUIViewController is. In fact, it is not actually even a type, it's only an inner name of the variable. And there is no way compiler could infer the type of it (to use with as operator). Hence, one of the proper ways to achieve what you are trying is by using generics. Consider generic T as a pattern which meets certain condition.
You need to change UIViewController to UIViewController.Type. For more see here
because a parameter of UIViewController can accept a UIViewController instance e.g. UIViewController(). However you need to get its type information (you don't need an instance), therefore it has to be a parameter of type UIViewController.Type so the value you pass can be something like SomeUIViewControllerSubclass.self which is NOT an instance...
So you have to do this:
func goToPage(goto storyBoardId: String, ofType typeUIViewController: UIViewController.Type) -> UIViewController {
let storyBoard: UIStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
let newPage = storyBoard.instantiateViewController(withIdentifier: storyBoardId) as! typeUIViewController
self.present(newPage, animated: true, completion: nil)
}

Swift popToViewController

Good day guys, I'm learning Swift, needed some help here.
The user are signing up and selected their image. Upon dismissing the image picker, I would like to have the ComposeViewController appear.
Here is the code:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: NSDictionary!) {
let pickedImage:UIImage = info.objectForKey(UIImagePickerControllerOriginalImage) as UIImage
//Scale Down Image
let scaledImage = self.scaleImageWith(pickedImage, and: CGSizeMake(100,100))
let imageData = UIImagePNGRepresentation(scaledImage)
let imageFile:PFFile = PFFile(data: imageData)
PFUser.currentUser().setObject(imageFile, forKey: "profileImage")
PFUser.currentUser().saveInBackgroundWithTarget(nil, selector: nil)
picker.dismissViewControllerAnimated(true, completion: nil)
//this is the line seems to have problem.
self.navigationController?.popToViewController(ComposeViewController, animated: true)
}
Then I got these error:
ComposeViewController.Type' is not convertible to 'UIViewController
Expected member name or constructor call after type name
It has suggestion to fix by putting () after ComposeViewController but then it gives out more errors after fixing.
Hope someone could help. Thanks! :-)
let controllers = self.navigationController?.viewControllers
for vc in controllers! {
if vc is YourVC {
_ = self.navigationController?.popToViewController(vc as! YourVC, animated: true)
}
}
I know this is old, but it's like what Saqib said, you can't pop to a viewcontroller that doesn't exist yet.
A lot of the answers here seem to be from people that didn't read your question, just the title. I'll leave this code here in case it helps anyone.
let vcIndex = self.navigationController?.viewControllers.indexOf({ (viewController) -> Bool in
if let _ = viewController as? ComposeViewController {
return true
}
return false
})
let composeVC = self.navigationController?.viewControllers[vcIndex!] as! ComposeViewController
self.navigationController?.popToViewController(composeVC, animated: true)
There's a method that lets you get access to an array of all the ViewControllers on the current stack, and you can capture the one you want by using its index, for instance:
let switchViewController = self.navigationController?.viewControllers[1] as! ComposeViewController
self.navigationController?.popToViewController(switchViewController, animated: true)
if let composeViewController = self.navigationController?.viewControllers[1] {
self.navigationController?.popToViewController(composeViewController, animated: true)
}
I ended up replaceing the following code inside the main view and it works. I'm not sure if this is the right way, would you mind giving me some comments?
//self.navigationController?.popToViewController(ComposeViewController, animated: true)
let switchViewController = self.storyboard?.instantiateViewControllerWithIdentifier("view2") as ComposeViewController
self.navigationController?.pushViewController(switchViewController, animated: true)
I defined "view2" as the destination storyboard ID.
What I found more useful was to do a first lookup with viewControllers, that way you get the first instance you find in the stack, without having to guess the actual index.
e.g.
let mainViewControllerVC = self.navigationController?.viewControllers.first(where: { (viewcontroller) -> Bool in
return viewcontroller is ComposeViewController
})
if let mainViewControllerVC = mainViewControllerVC {
navigationController?.popToViewController(mainViewControllerVC, animated: true)
}
For Swift 4.0 and above Using Filter
guard let VC = self.navigationController?.viewControllers.filter({$0.isKind(of: YourViewController.self)}).first else {return}
self.navigationController?.popToViewController(VC, animated: true)
navigation controller maintains the stack of views you are pushing. Its like a Last in first out queue.
In order to pop to ComposeViewController, that view must already exist in the queue and you should have reference to it.
You will need to pass the instance of ComposeViewController. for simplicity you might save that reference in appdelegate. (this approach is not recommended)
for (var i = 0; i < self.navigationController?.viewControllers.count; i++)
{
if(self.navigationController?.viewControllers[i].isKindOfClass(DestinationViewController) == true)
{
self.navigationController?.popToViewController(self.navigationController!.viewControllers[i] as! DestinationViewController, animated: true)
break;
}
}
In Swift 4.1 and Xcode 9.4.1
Suppose if you moved from 1st ViewController to 2nd, then 2nd to 3rd. Now if you want to come back from 3rd to 1st directly this code is enough.
if let composeViewController = self.navigationController?.viewControllers[1] {//Here you mention your view controllers index, because navigation controller can store all VC'c in an array.
print(composeViewController)
self.navigationController?.popToViewController(composeViewController, animated: true)
}