How To Dismiss Popover From Destination View Controller in Swift - swift

I have a main view controller that has been setup in Interface Builder to open a table view controller via popover segue connected to a button. I want to be able to dismiss the popover when an item inside of my popover table view is selected in didSelectRowAtIndexPath. In Objective-c I can just typecast the the segue in the prepareForSegue delegate to a UIStoryboardPopoverSegueand pass along its UIPopoverController to the table view controller. However, in Swift my downcast fails because it sees the segue as type UIStorybaordPopoverPresentationSegue (when stepping through with the debugger) which doesn't appear to be a public API.
Here's my code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "ShowCollectionsSegue" {
if let collController:CollectionsTableViewController! = segue.destinationViewController as? CollectionsTableViewController {
if let popoverSegue = segue as? UIStoryboardPopoverSegue { // <-- This fails
collController.popover = popoverSegue.popoverController
}
}
}
}
How do I coerce the segue to a UIStoryboardPopoverSegue in order to access its popoverController property?
I'm open to solving the problem of dismissing the popover in response to a table view cell tap a different way, but it seems that when using a segue from the storyboard, the only way to dismiss the popover is by holding onto a reference to the popover controller somehow and the only way to do that as far as I can tell is to cast the segue to a popover segue which Swift doesn't want to let me do. Any ideas?

A strange problem, indeed. I noticed in the documentation, that UIStoryboardPopoverSegue does not inherit from any class. That explains why the cast does not work - UIStoryboardSegue is not its superclass. So I just tried to create a new object - it looks weird but works:
let popoverSegue = UIStoryboardPopoverSegue(
identifier: segue.identifier,
source: self,
destination: segue.destinationViewController as UIViewController)
println("Is there a controller? \(popoverSegue.popoverController.description)")
// YES !!
EDIT
But this controller will not dismiss the popover :(
The fix is to specify the segue in Interface Builder as "Deprecated Segues : Popover". Then the code would be as expected
let popoverSegue = segue as UIStoryboardPopoverSegue
if let destination = segue.destinationViewController as? TableViewController {
destination.delegate = self
self.popoverController = popoverSegue.popoverController
}

Related

Pass data from ViewController (not Detail) to MasterViewController in UISplitView Swift

How to pass data from ViewController (VC) to MasterView (MV) in UISplitView? As I figured out I can't use segue because it goes from VC to SplitView, not to MV.
Check please picture below
screenshot of storyboard
UPD:
Thanks Stepan for the help, I have figured out.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toMessurementVM"{
let splitVC = segue.destinationViewController as! UISplitViewController
let navVC = splitVC.childViewControllers.first as! UINavigationController
let vc = navVC.childViewControllers.first as! MessurementTagsTableView
vc.ingredient = passingIngredient
}
}
I am not sure that this is right way, but it works.
There are many examples of how to pass data from Detail to Master and vice-versa, but there is nothing for my case.
Thanks
1) don't pass data! just use pointers.
2) you can get SplitViewController with superview from MasterView or ViewController
3) register for notification in controller you need and then just send notification that some data ready.
4) on destination controller which catch your notification - just get data with pointer with get superview from controller, cast it to SplitViewController and then you can get master or detail from it with no problems, and then get data from it by pointer.
5) use debugger stop and see what you have in superview, and then You will know exact how to act )
6) make sure to call all UI operation on main thread.

Swift - Access different View Controllers in App Delegate

In app delegate, with a simple app having only 2 screens:
first screen is a Table View Controller embedded in Navigation Controller
second screen is a View Controller which is used to add items to the first screen table via protocol/delegate/segue
And this is the code for didFinishLaunchingWithOptions in app delegate that I can reference the first screen as viewController[0]:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let navController = self.window?.rootViewController as! UINavigationController
let courseListController = navController.viewControllers[0] as! CourseListController
courseListController.managedObjectContext = self.managedObjectContext
return true
}
How can I reference the screens at above, below and next to the center sreen? Please suggest me a solution. Thank you!
It's important to keep in mind that the view controller objects for all of the "peripheral" view controllers in your story board won't actually exist until the segue to get to them is executed, so there's no way to get access to them directly from the app delegate. Instead, you need to push state to each child view controller as it's created, from whatever the source view controller is. Segues are the appropriate way to do this.
You will probably want to assign each of the segues from the central view controller a unique segue identifier in Interface Builder. Click on the segue, then enter it here:
In the central view controller, implement prepareForSegue(_:sender:), doing something like the following:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject) {
switch segue.identifier {
case "SegueIdentifier1":
guard let destination = segue.destinationViewController as? ViewController1 else {
return
}
// set up your view controller here
case "SegueIdentifier2":
guard let destination = segue.destinationViewController as? ViewController2 else {
return
}
// set up your view controller here
// add additional segues as required
default:
break // unknown segue
}
}
Go to each view Controller you want to reference and in the identity inspector, add some string to its StoryBoard ID.
next to reference it from the new ViewController (say, XViewController) to (say, YViewController)
do this :
var referencedViewController = self?.storyboard.
instantiateViewControllerWithIdentifier("referenceViewID") as! YViewController
self.presentViewController(referencedViewController,
animated: true, completion: nil)

Navigation bar fails to unwind segue along with view controller

I have three main view controllers A, B, C, which are presented in the order by show segue. All the three view controllers are embedded in their own navigation controller. After dismissing a modal controller D presented on C, I would like to go back to controller A and reload data before it appears.
I referred to the solution from this question to unwind segue and wrote an action in controller A (I use AViewController to represent the class of controller A):
#IBAction func prepareForUnwindSegue(segue: UIStoryboardSegue?) {
if let destController = segue?.destinationViewController as? AViewController {
destController.reload()
}
}
override func canPerformUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject) -> Bool {
return self.respondsToSelector(action)
}
I dragged-exit between controller A and C by the above action, and name the unwind segue unwindToA. Then in the class of controller C, named CViewcontroller, I call the modal view D in one ActionSheet as follow:
let presentDAction = UIAlertAction(title: "Present D", style: .Default, handler: {(_) -> Void in
let controller: AnyObject? = self.storyboard!.instantiateViewControllerWithIdentifier("DView")
if let c = controller as? DViewController {
let navi = UINavigationController(rootViewController: c)
navi.modalPresentationStyle = UIModalPresentationStyle.FullScreen
// the following block wait and execute after view controller D dismissed
c.refreshAndUnwind = {[weak self](refresh) in
if let weakSelf = self {
if(refresh) {
weakSelf.performSegueWithIdentifier("unwindToA", sender: weakSelf)
}
}
}
}
})
The view controller did successfully unwind along with reloading data. However, it seems that the navigation bar remains still on controller C, and its title disappear. I have tried to drag-exit the navigation controller between A and C instead and modify the unwind segue action call to weakSelf.navidationController!.performSegueWithIdentifier("unwindToA", sender: weakSelf), but it doesn't work. Did I do anything wrong on my segue unwinding that cause this problem?
Sometimes using storyboard is not the best way to do things. I'd do this like this in your case:
var viewControllers = self.navigationController!.viewControllers
var controllerA = viewControllers[0] as! ControllerA
controllerA.reloadStuff()
var newViewControllers = [ controllerA ]
self.navigationController!.setViewControllers(newViewControllers, animated: true)
The code is much cleaner this way. You can set the navigationItem's title in viewDidAppear on each view if you have problems with it, you can be sure that it will be always called.
So the problem is simple: After I tried to modify my storyboard and remove those not-necessary navigation controllers, it performs just fine.

Swift segue performed multiple times

A segue in my app is being called multiple times, which is causing the view to continuously "load" until the app stops calling the segue function. It does this about 4 times until it's finished. After placing breakpoints in the code I noticed that the app is bouncing between these two functions:
if success {
self.performSegueWithIdentifier("startGame", sender: "user")
}
That is the action that triggers the segue (the user swipes something). It then goes to the next breakpoint:
if (segue.identifier == "startGame") {
let destinationNavigationController = segue.destinationViewController as! GameViewController
destinationNavigationController.user = self.users[self.currentUser]
}
The app goes back and forth between the two sections about 4 times. When I created the segue in my storyboard I made sure to wire the segue from the view controller itself (not a table/UI view) to the destination view.
What else can I do to fix this?
Thanks!
In storyboard, create a segue by holding control and dragging from the yellow icon of the 1st view controller to the 2nd view controller.
Give it an id, and the kind should be push if they are embedded in a navigation controller.
In your 1st view controller class:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "startGame" {
let viewController = segue.destinationViewController as! GameViewController
// this next view controller should have property that will store passed value
viewController.user = self.users[self.currentUser]
}
}
When you want to move to the next view controller:
performSegueWithIdentifier("startGame", sender: self)

How can I use the selected data from a UIDatePicker in another view in IOS8?

I'm very new to IOS 8 and programming in general. I would like to populate a UIButton's label text with the value from a UIDatePicker. When the user presses the button, a popover appears with a date picker. I have created a new view controller imbedded in a navigation controller for my date picker. They are connected using a popover segue. My question is once the user selects a date, how can I transmit this information back to the original view controller to show the value on the button label. Any help would be greatly appreciated.
To pass a value between 2 viewcontrollers using segues is easies done like this:
class VC1:UIViewController {
#IBAction func next() {
let valueToPass = NSDate() // Could be anything that conforms to AnyObject
self.performSegueWithIdentifier("segueID", sender: valueToPass) // Use this method unless you're connecting a button to a viewcontroller in storyboard
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let vc2 = segue.destinationViewController as VC2 // The viewcontroller you're going to
vc2.date = sender as NSDate // If you connected button to vc in storyboard sender will be nil, so in that case just set the vc2.date directly here
}
}
class VC2:UIViewController {
var date:NSDate! // Property you want to pass to
}