I thought this would be simple, but I'm really struggling with it. I have a map view in a tab bar controller, and I have a viewController that when you tap a button, it is supposed to save the user's coordinates and put an annotation on the map. I thought I could just make a boolean newPost, and set it as true, and then in the viewDidLoad method for my map view, say,
if newPost {
var newAnnotation = MKPointAnnotation()
var newPostCoordinate = CLLocationCoordinate2DMake(userPosts.last!.postLatitude, userPosts.last!.postLongitude)
newAnnotation.coordinate = newPostCoordinate
newAnnotation.title = userPosts.last!.postTitle
mainMapView.addAnnotation(newAnnotati
newPost = false
}
Unfortunately, the mapView doesn't reload when I go back to it after going to the new post viewController, so the viewDidLoad method doesn't get called again.
So instead, I created a new method, and tried to call it when the post button on the new post viewController is tapped. This is where I got stuck. I thought it would just be:
mapViewController.theMethodIWantToRun()
but I end up with a "missing argument for parameter #1 in call" error. My method doesn't have any arguments or return anything, so I don't think I should need arguments...
I feel like this is a pretty dumb question, but if I could figure it out, I wouldn't be here.
If you think there's a better way to do what I'm trying to do, feel free to let me know! Thanks!
The easiest way to do that is to move your newPost condition in viewWillAppear method. Otherwise the best approach to send data between controllers is by using delegation pattern
Related
The question might sound complicated, so let me ease the stress*
Lets say I have 2 tab bar tabs (Home1, Home2)
Both (Home1, Home2) have navigational controllers with two view controllers each
Home1(Navigation controller -> VC11 -> VC12)
Home2 (Navigation controller -> VC21 -> VC22)
Easy right? Well, not quite
What I want to do is pass data from [Home1: VC12 to Home2: VC22].
My coding efforts:
tabBarController!.selectedIndex = 1
self.navigationController!.popToViewController(navigationController!.viewControllers[1] as! (Home2: VC22), animated: false)
let navController = self.tabBarController?.viewControllers![1] as! UINavigationController
navController.popToViewController((Home2: VC22) as! UIViewController, animated: true)
for oneView in (self.navigationController?.viewControllers)! {
if (oneView.isKind(of: (Home2: VC22).classForCoder())) {
let friendView = oneView as! (Home2: VC22)
friendView.testing = "Worked?"
}
}
ANYHELP IS WELCOMED!! Total hours spent: 8hrs
Your pass data task is going though this process
V12 --(pop)--> V11 --(switch)--> V21 --(push)--> V22
If you choose to pass data, you will need to pass it though all these three transactions and that's lots of coding and it's hard to practice.
Instead of passing data around, a better way to accomplish your goal is to store the data in V12, and then load it from V22. So now all you need to worry about is how to transfer user from V12 to V22.
You only need two lines of code to store and read data from UserDefaults
On your V12, before you are doing the above transactions, first save your data like this
UserDefaults.standard.set("yourStringData", forKey: "data")
Then when you reach VC22, from viewDidLoad, read stored info in this way
UserDefaults.standard.string(forKey: "data")
For your view transaction part, popToViewController is for going from a child view controller to it's parent. So when you switch your tab and get the UINavigationController, the topViewController must be VC21. So to go to VC22 given that you connect them though a storyboard segue, simply call it like this
let navController = self.tabBarController?.viewControllers![1] as! UINavigationController
let VC21 = navController.topViewController!
VC21.performSegue(withIdentifier: "segue", sender: nil)
You could also subclass your tabbar and create a variable to store your data there. Then both VC's would have access to the data. This way also alleviates the possible need for a singleton.
It sounds like you need to rethink your approach. It depends on the way you setup your app but it makes sense to store this data somewhere else besides directly passing it from one VC to another. Here are some options:
Singleton that stores your data and you pass that around from one VC to another so that if VC12 stores data in the Singleton, then VC22 can access it easily since it also has access to the singleton.
Have the Container View Controller store the data to be shared amongst its children view controllers. So, in this example, the TabBarController can store this data.
Use UserDefaults as a poor-mans Singleton/Persistence solution.
Use a full blown Data Persistence layer using SQLite, Core Data, or Realm for example to store and retrieve data.
... there are others but I think one of these solutions can work well for you depending on your needs.
I'm working on a little project in spritekit and can't quite figure something out. I am animating a sprite using SKAction.animateWithTextures and moving through and array. Works fine just like it should. The issue is that I would like to a function that starts when the animation starts and one when it ends. I see there is a .animationDidStart(CAAnimation), but because what I'm doing is not a CAAnimation I can't really use it. Is there something like this for the method I'm using? As you can may or may not be able to tell I'm still rather new to swift. Thanks for any help in advance.
I'd create a sequence of actions. First a block action to call your method you want called when the animation starts, then your animateWithTextures action, finally another block action that calls your finished method.
let startAction = SKAction.runBlock {
self.startAnimation()
}
let textureAction = SKAction.animateWithTextures...
let finishedAction = SKAction.runBlock {
self.finishedAnimation()
}
SKAction.sequence([startAction, textureAction , finishedAction])
This is all purely for educational purposes, to help me get a better understanding of how Parse as well as Swift operates.
I would like to make it so a user is only able to like an item once (not being able to hit a button multiple times), as currently, I'm utilizing an anonymous system with Parse.
Would I essentially use an if method with PFUser.CurrentUser() in the likeButton method to halt a user from hitting like again or would I use NSUserDefaults?
I'm not able to post code currently as I'm not near my laptop, however I could later if it helps. Still curious if I could get some info before that however.
Sample code I found on here from a previous question, which essentially implements the same idea.
#IBAction func likeButton(sender: AnyObject) {
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
let object = objectAtIndexPath(hitIndex)
object.incrementKey("count")
object.saveInBackground()
self.tableView.reloadData()
}
Would I call NSUsersDefaults to stunt the user from hitting it more than once?
Instead of calling saveInBackground(), you'd better call the method saveInBackgroundWithBlock: instead. So the strategy is very simple:
First of all, define a 'busy' flag for the object (For example: savingInBackground) and store it wherever you like ( If you are showing 1 item then simply declare a Bool property / If you are showing a list of Item then declare a Dictionary with format ["objectID/Index": Bool]). This flag should be set to true for the item being saved in the background.
Whenever use taps on a Like button
If current item's savingInBackground flag is true, then do nothing
Else:
Set item's savingInBackground to true
Increase Like count and Call saveInBackgroundWithBlock:
In the completion block of saveInBackgroundWithBlock:, set savingInBackground back to false.
I am on train now so I can't write example code, but I hope that it's clear enough to help you to achieve what you want.
I have a NSDocument-based app, and I'd like my window positions to be saved and restored when re-opening documents. Apple's documentation on this is pretty sparse, but what I've been able to piece together is that at some point, something in the app needs to call NSWindow.setFrameUsingName() and NSWindow.setFrameAutosaveName().
What I haven't quite figured out is what point this needs to happen at, and what things need to do this. For example, this doesn't work at all:
// In my NSDocument class
override func windowControllerDidLoadNib(aController: NSWindowController) {
super.windowControllerDidLoadNib(aController)
// Add any code here that needs to be executed once the windowController has loaded the document's window.
aController.window?.setFrameUsingName("MainWindow")
aController.window?.setFrameAutosaveName("MainWindow")
}
I've read various different pieces of documentation or forum answers that point to awakeFromNib() to be another area to do this, but I can't get that to work either.
I'm also confused / worried that this is somehow being affected by Auto Layout or something I've done wrong in Interface Builder - for example, this is how my window is set up in IB:
I don't particularly want my window centered, but the other options seem to lock it in place in fixed horizontal or fixed vertical positions, which I also don't really want. A side effect of having my window be centered is that my document windows no longer cascade, which I neither want nor can seem to stop from happening (note that windowController.shouldCascadeWindows = true isn't helping either).
So - what's going on here? I'm finding knowledge on this topic to be particularly unclear or misleading, and likely out of date for Cocoa development 2015, so a modern refresher on this would be great.
Step 1: In IB, give the window an autosave name.
Step 2: There is no step 2.
The easiest place to set the name of the autosave name for the window frame is probably in your implementation of NSDocument.
If you override makeWindowControllers() as part of the implementation, you are creating the window controller(s) manually, and thus can set the name there:
override func makeWindowControllers() {
let storyboard = NSStoryboard(name: NSStoryboard.Name("MyDocumentStoryboard"), bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("MyDocument Window Controller")) as! MyDocumentWindowController
// this assumes you have a stored property or a computed property `someUniqueIdentifier` that is as unique as possible for your document
// and that you store on disk together with the rest of the document properties
windowController.windowFrameAutosaveName = NSWindow.FrameAutosaveName(rawValue: someUniqueIdentifier)
self.addWindowController(windowController)
}
If you instantiate your document window by overriding windowNibName, you should instead override the method windowControllerDidLoadNib(_:), to set the autosave name on the window. I have not tested this code, but I assume it will work:
func windowControllerDidLoadNib(_ windowController: NSWindowController) {
// this assumes you have a stored property or a computed property `someUniqueIdentifier` that is as unique as possible for your document
// and that you store on disk together with the rest of the document properties
windowController.windowFrameAutosaveName = NSWindow.FrameAutosaveName(rawValue: someUniqueIdentifier)
}
I am trying to use swift's shouldPerformSegueWithIdentifier() method, but it accepts 2 arguments. These are (identifier: String!, sender:AnyObject)
My main goal is to execute the code when pressing a login button in my storyboard, and depending of a series of checks return TRUE or FALSE, depending if whether the correct username and password were provided. So here are my questions:
What am I supposed to use as the identifier? Apple's documentation it explains that the identifier is a string that identifies the triggered segue. So suppose that my segue had the name of loginSegueProcess. How could I use it in my ViewController tat is assigned to my UIView? The thing is that I declare the method in my code and it requires me to specify both arguments (identifier & sender). How could I provide the arguments?
Will this method actually fulfill my needs? By that I mean if it will indeed stop the segue transition whenever my Login button is clicked and depending on whether the correct credentials were provided it is going to take you to the next View or it will show, say for example, an AlertView.
Finally, I was thinking that the performSegueWithIdentifier(args) method would help me as well. Does anybody know the difference between them?
Thanks a lot in advance!
isn't it what you want to do?
override func shouldPerformSegueWithIdentifier(identifier: String!, sender: AnyObject!) -> Bool {
if identifier == "LoginSuccessSegue" { // you define it in the storyboard (click on the segue, then Attributes' inspector > Identifier
var segueShouldOccur = /** do whatever you need to set this var to true or false */
if !segueShouldOccur {
println("*** NOPE, segue wont occur")
return false
}
else {
println("*** YEP, segue will occur")
}
}
// by default, transition
return true
}
You may not invoke shouldPerformSegueWithIdentifier() method by yourself. It will be automatically called just before transition to the next view giving a chance to determine wether the transition should take place or. You may conditionally return YES/NO from this method. If your condition does't involve any sever call,a simple logical checking this method will be enough for you.
performSegueWithIdentifier() is used to invoke a segue programmatically. Consider the above case with a network call, you may return NO from shouldPerformSegueWithIdentifier() initially since authentication is going on. After getting the response from server if it success you can call the segue to execute with performSegueWithIdentifier (Here the identifier is the ID you have given in the storyboard). Before make sure you are supposed to return YES from shouldPerformSegueWithIdentifier().
Now a third case if your segue is connecting from the login button(You have to connect it from the controller itself). The checking of shouldPerformSegueWithIdentifier is no more required. You can just call the segue with performSegueWithIdentifier() after getting the success response from server