Swift: No Idea How to Get Back the Selected Value from a Popover to the Calling Controller

SWIFT: No idea how to get back the selected value from a popover to the calling controller

I made quick example, hope it helps:

// This is you popover's class

@objc protocol CurrencySelectedDelegate {
func currencySelected(currName: String)
}

class MyPopOverController: UIViewController {

weak var delegate: CurrencySelectedDelegate?

@IBAction func readyButtonPressed(sender: AnyObject) {

// Do what you want

delegate?.currencySelected("Euro/Dollar etc....")

// close popover
}
}

// ViewController
class ViewController: UIViewController, CurrencySelectedDelegate {

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "mySegue" { // your identifier here
let controller = segue.destinationViewController as! MyPopOverController
controller.delegate = self
}
}

}

And remember just declare that currencySelected function in your ViewController.

Popover with embedded navigation controller doesn't respect size on back nav

I was struggling with the same issue. None of the above solutions worked for me pretty nicely, that is why I decided to do a little investigation and find out how this works.

This is what I discovered:

  • When you set the contentSizeForViewInPopover in your view controller it won't be changed by the popover itself - even though popover size may change while navigating to different controller.
  • When the size of the popover will change while navigating to different controller, while going back, the size of the popover does not restore
  • Changing size of the popover in viewWillAppear gives very strange animation (when let's say you popController inside the popover) - I'd not recommend it
  • For me setting the hardcoded size inside the controller would not work at all - my controllers have to be sometimes big sometimes small - controller that will present them have the idea about the size though

A solution for all that pain is as follows:

You have to reset the size of currentSetSizeForPopover in viewDidAppear. But you have to be careful, when you will set the same size as was already set in field currentSetSizeForPopover then the popover will not change the size. For this to happen, you can firstly set the fake size (which will be different than one which was set before) followed by setting the proper size. This solution will work even if your controller is nested inside the navigation controller and popover will change its size accordingly when you will navigate back between the controllers.

You could easily create category on UIViewController with the following helper method that would do the trick with setting the size:

- (void) forcePopoverSize {
CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f, currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
}

Then just invoke it in -viewDidAppear of desired controller.

Swift, How to use data passed back from VC in other functions?

Unless I’m missing something you seem to be 90% of the way there. You’ve got popupValueSelected getting a value. If you store the value off to a class level variable you can then use it later. Let me know if I’m not understanding your issue fully.

How to Dismiss a Storyboard Popover

EDIT: These problems appear to be fixed as of iOS 7.1 / Xcode 5.1.1. (Possibly earlier, as I haven't been able to test all versions. Definitely after iOS 7.0, since I tested that one.) When you create a popover segue from a UIBarButtonItem, the segue makes sure that tapping the popover again hides the popover rather than showing a duplicate. It works right for the new UIPresentationController-based popover segues that Xcode 6 creates for iOS 8, too.

Since my solution may be of historical interest to those still supporting earlier iOS versions, I've left it below.


If you store a reference to the segue's popover controller, dismissing it before setting it to a new value on repeat invocations of prepareForSegue:sender:, all you avoid is the problem of getting multiple stacking popovers on repeated presses of the button -- you still can't use the button to dismiss the popover as the HIG recommends (and as seen in Apple's apps, etc.)

You can take advantage of ARC zeroing weak references for a simple solution, though:

1: Segue from the button

As of iOS 5, you couldn't make this work with a segue from a UIBarButtonItem, but you can on iOS 6 and later. (On iOS 5, you'd have to segue from the view controller itself, then have the button's action call performSegueWithIdentifier: after checking for the popover.)

2: Use a reference to the popover in -shouldPerformSegue...

@interface ViewController
@property (weak) UIPopoverController *myPopover;
@end

@implementation ViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// if you have multiple segues, check segue.identifier
self.myPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if (self.myPopover) {
[self.myPopover dismissPopoverAnimated:YES];
return NO;
} else {
return YES;
}
}
@end

3: There's no step three!

The nice thing about using a zeroing weak reference here is that once the popover controller is dismissed -- whether programmatically in shouldPerformSegueWithIdentifier:, or automatically by the user tapping somewhere else outside the popover -- the ivar goes to nil again, so we're back to our initial state.

Without zeroing weak references, we'd have to also:

  • set myPopover = nil when dismissing it in shouldPerformSegueWithIdentifier:, and
  • set ourself as the popover controller's delegate in order to catch popoverControllerDidDismissPopover: and also set myPopover = nil there (so we catch when the popover is automatically dismissed).

How to dismiss UIPopover from a button in the Popover

Idea is simple. YourViewController - it's viewController of UIPopoverController. MainViewController - controller where you create UIPopoverController

  1. Declare protocol in YourViewController with dismiss method
  2. Declare property of type id<DismissDelegateProtocol> in YourViewController
  3. Declare support of DismissDelegateProtocol in MainViewController
  4. Implement dismiss method of DismissDelegateProtocol in MainViewController
  5. When you create YourViewController in MainViewController set delegate property (yourViewController.delegate = self;)
  6. In action, that response to button touching call delegate method: [self.delegate dismissWithData:dataToTransfer];

In code it should be like this:

In MainViewController.h:

#import "YourViewController.h"
@class MainViewController: UIViewController < DismissPopoverDelegate >

In MainViewController.m:

- (void) dismissPopover:(NSObject *)yourDataToTransfer
{ /* Dismiss you popover here and process data */ }

...
// Some method, when you create popover
{
YourViewController *vc = ... ;
vc.delegate = self; // this delegate property should be declared as assign
}

In YourViewController.h:

@protocol DismissPopoverDelegate
- (void) dismissPopover:(NSObject *)yourDataToTransfer;
@end

@class YourViewController : UIViewController
{
id<DismissPopoverDelegate> delegate;
}

@property (nonatomic, assign) id<DismissPopoverDelegate> delegate;

In YourViewController.m:

- (void) methodWhenYouWantToDismissPopover
{
[self.delegate dismissPopover:data];
}

Question about passing data from an API into a view controller after user query - Swift

First, on WeatherManager, declare your delegate as weak to avoid reference cycle

weak var delegate : WeatherManagerDelegate?

Second, after you decode the data, you have to call the delegate and pass the data, in this case,the WeatherModel you just created using one of your delegate methods

 do {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)



let clouds = decodedData.clouds
let lowCloudsType = (clouds.count > 0 ? clouds[0]?.type : nil) ?? "N/A"
let midCloudsType = (clouds.count > 1 ? clouds[1]?.type : nil) ?? "N/A"
let highCloudsType = (clouds.count > 2 ? clouds[2]?.type : nil) ?? "N/A"
let lowCloudsAlt = (clouds.count > 0 ? clouds[0]?.altitude : nil) ?? 0
let midCloudsAlt = (clouds.count > 1 ? clouds[1]?.altitude : nil) ?? 0
let highCloudsAlt = (clouds.count > 2 ? clouds[2]?.altitude : nil) ?? 0
let reportingStationVar = decodedData.station ?? "N/A"
let windGustValue = decodedData.wind_gust?.value ?? 0
let windSpeedValue = decodedData.wind_speed?.value ?? 0
let windDirectionValue = decodedData.wind_direction?.value ?? 999
let visibilityValue = decodedData.visibility?.value ?? 0
let flightRulesValue = decodedData.flight_rules ?? "N/A"

let weather = WeatherModel(lowestCloudsType: lowCloudsType , lowestCloudsAlt: lowCloudsAlt, middleCloudsType: midCloudsType , middleCloudsAlt: midCloudsAlt, highestCloudsType: highCloudsType , highestCloudsAlt: highCloudsAlt, reportingStation: reportingStationVar, windGust: windGustValue, windSpeed: windSpeedValue, windDirection: windDirectionValue, visibility: visibilityValue, flightRules: flightRulesValue)

delegate?.didUpdateWeather(self, weather: weather)

return weather

} catch {
delegate?.didFailWithError(error: error)
return nil
}

On ReportViewController, assign self as your WeatherManagerDelegate on viewDidLoad(), then you have to implement the delegate function didUpdateWeather by updating your labels with corresponding values -- which you already started

class ReportViewController: UIViewController, WeatherManagerDelegate {

var weatherManager = WeatherManager()

@IBOutlet weak var flightRulesTitleLabel: UILabel!

@IBOutlet weak var flightRulesValueLabel: UILabel!

@IBOutlet weak var visibilityValueLabel: UILabel!

@IBOutlet weak var altimeterValueLabel: UILabel!

@IBOutlet weak var cloudsTitleLabel: UILabel!

@IBOutlet weak var cloudsType1Label: UILabel!

@IBOutlet weak var cloudsAltitude1Label: UILabel!

@IBOutlet weak var cloudsType2Label: UILabel!

@IBOutlet weak var cloudsAltitude2Label: UILabel!

@IBOutlet weak var cloudsType3Label: UILabel!

@IBOutlet weak var cloudsAltitude3Label: UILabel!

@IBOutlet weak var windGTextLabel: UILabel!

@IBOutlet weak var windSpeedValueLabel: UILabel!

@IBOutlet weak var windGustValueLabel: UILabel!

@IBOutlet weak var windFromTextLabel: UILabel!

@IBOutlet weak var windDirectionValueLabel: UILabel!

@IBOutlet weak var remarksValueLabel: UILabel!

override func viewDidLoad() {
super.viewDidLoad()

weatherManager.delegate = self
}


func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) {
DispatchQueue.main.async {
self.flightRulesValueLabel.text = weather.flightRules
self.cloudsType1Label.text = weather.lowestCloudsType
}

}

Presenting a popover without segue -- Popover is still full screen

You need to set the modalPresentationStyle to .popover before you access the popoverPresentAtionController property:

From the documentation:

If you created the view controller but have not yet presented it, accessing this property creates a popover presentation controller when the value in the modalPresentationStyle property is UIModalPresentationStyle.popover. If the modal presentation style is a different value, this property is nil.

As you haven't set the modal presentation style before you set the delegate, no popover presentation controller is created. The popover controller will be created when you set the .sourceView, as the presentation style is now .popover but the delegate won't be set on this instance.

Also, you can use a segue with a button you create in code by using performSegue in the buttons action handler code.



Related Topics



Leave a reply



Submit