Reload MainViewController when PopViewController dismissed - swift

I have a main view controller and pop up controller.Please refer screen shots.
Code :
#IBAction func show(sender: AnyObject) {
var popView = popupviewcontroller(nibName:"popview",bundle:nil)
var popController = UIPopoverController(contentViewController: popView)
popController.popoverContentSize = CGSize(width: 450, height: 450)
popController.presentPopoverFromRect(sender.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Down, animated: true)
}
For the popupViewcontoller i used .xib.
When press save button data saved to core data.
Lets come to my problem, in my mainViewController i fetched data and fill them in dynamically created lables.That occurred when view load.I want to reload mainViewController when close button form popViewController pressed.
I tried within the close button my code are here, i just tried to reload the mainVc :
var mainVC = mainviewcontroller()
#IBAction func close(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
//mainVc.viewDidLoad()
mainVC.reloadInputViews()
}
Does not give output.
Conclusion : I want a way to refresh view controller from another view in swift.
Thanks in advance.

Using UITableView only we can reload the data!,So we have to use table view custom cell textfield or Label. Now we can able to reload our custom textfield data.

Does your main ViewController use a UITableView?
You can use viewWillAppear and get the data again and use tableView.reloadData() to reload the data.
EDIT:
with var mainVC = mainviewcontroller() you're just making a new instance of your MainViewController. If you want to use reloadInputViews(), you can put it in viewWillLoad.

You should use protocol for it. You can read more about protocols here https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
You can make protocol in PopViewController
protocol PopViewControllerProtocol {
static var valuesChanged()
}
Later you should implement this protocol in MainViewController and refresh data.
Example:
File UIPopoverController.swift
protocol UIPopoverControllerDelegate{
func valuesChanged(changedValue:String)
}
class UIPopoverController: UIViewController {
var delegate: UIPopoverControllerDelegate! = nil
#IBAction func close(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
//mainVc.viewDidLoad()
mainVC.valuesChanged("some value")
}}
File MainViewController.swift
class MainViewController: UIViewController, UIPopoverControllerDelegate
{
func valuesChanged(changedValue:String) {
//this will be called when popuviewcontroller call valueschanged on delegate object
}
#IBAction func show(sender: AnyObject) {
var popView = popupviewcontroller(nibName:"popview",bundle:nil)
var popController = UIPopoverController(contentViewController: popView)
popController.delegate = self;
popController.popoverContentSize = CGSize(width: 450, height: 450)
popController.presentPopoverFromRect(sender.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Down, animated: true)
}
}

With God Grace i found a solution that is , just create a Global variable
Var fetchedArray = Nsarray()
Then Write the following code in the popovoer controller save button
func save(){
// Write codes for saving data
let request = NSFetchRequest(entityName: "Enitity name")
request.returnsObjectsAsFaults = false
let Total = try? context.executeFetchRequest(request)
if let wrapResults = Total {
fetchedArray = wrapResults
}
}
Then fill the Labels by using fetchedArray.
Thanks

Swift 2, try
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle
viewDidLoad()
return.None
Works for me

Related

Pushing a navigation controller is not supported in swift , Move from Modal to Navigation

Similar like iPhone phoneBook, I want make view like VC(tableView) -> Modal -> navigationView. But I'm having some problems:
//vc(tableView)
#objc func addBtnClick(sender: UIButton){
let childVC = InformationView()
let fakeRootView = UINavigationController(rootViewController: childVC)
self.present(fakeRootView, animated: true){
print("rootView CompletionHandler")
}
Then save infomation in InformationView using realm:
#objc
func saveBtnClick(sender: UIButton){
content.phoneName = self.nameField.text!
content.phoneNum = self.phoneNumber.text!
try! realm.write {
realm.add(content, update: .all)
}
print(Realm.Configuration.defaultConfiguration.fileURL!)
table.tableView.reloadData()
self.presentingViewController?.dismiss(animated: true)
navInfoViewLoad()
func navInfoViewLoad(){
let childVC = contentView
self.navigationController?.pushViewController(childVC, animated: true)
}
After pressing saveBtnClick, I want to move to contentView. But I am getting an error:
Thread 1: Pushing a navigation controller is not supported
What is the problem?

Return control to function when modal viewcontroller dismissed

I need to present a modal VC that sets a property in my presenting VC, and then I need to do something with that value back in the presenting VC. I have to be able to pass pointers to different properties to this function, so that it's reusable. I have the code below (KeyPickerTableViewController is the modal VC).
It should work, except not, because the line after present(picker... gets executed immediately after the picker is presented.
How do I get my presenting VC to "wait" until the modal VC is dismissed?
#objc func fromKeyTapped(_ button: UIBarButtonItem) {
print("from tapped")
setKey(for: &sourceKey, presentingFrom: button)
}
#objc func toKeyTapped(_ button: UIBarButtonItem) {
print("from tapped")
setKey(for: &destKey, presentingFrom: button)
}
fileprivate func setKey(for key: inout Key!, presentingFrom buttonItem: UIBarButtonItem) {
let picker = KeyPickerTableViewController()
picker.delegate = self
picker.modalPresentationStyle = .popover
picker.popoverPresentationController?.barButtonItem = buttonItem
present(picker, animated: true, completion: nil)
if let delKey = delegatedKey {
key = delKey
}
}
You could use delegate pattern or closure.
I would do the following
1. I would not use inout pattern, I would first call the popover and then separately update what is needed to be updated
2. In KeyPickerTableViewController define property var actionOnDismiss: (()->())? and setting this action to what we need after initialisation of KeyPickerTableViewController
I could show it in code, but the abstract you've shown is not clear enough to come up with specific amendments. Please refer the illustration below.
import UIKit
class FirstVC: UIViewController {
var key = 0
#IBAction func buttonPressed(_ sender: Any) {
let vc = SecondVC()
vc.action = {
print(self.key)
self.key += 1
print(self.key)
}
present(vc, animated: true, completion: nil)
}
}
class SecondVC: UIViewController {
var action: (()->())?
override func viewDidLoad() {
onDismiss()
}
func onDismiss() {
action?()
}
}
While presenting VC, add dismissing modal VC action in its completion handler, so that Viewcontroller will be presented after dismissal is completed
present(picker, animated: true, completion: { (action) in
//dismissal action
if let delKey = delegatedKey {
key = delKey
}
})

Can a UIViewController that is presented as a popover be its own popoverPresentationController delegate?

In the project shown below there is an InitialViewController that has a single button labeled "Show Popover". When that button is tapped the app is supposed to present the second view controller (PopoverViewController) as a popover. The second view controller just has a label saying "Popover!".
This works fine if the InitialViewController takes care of instantiating PopoverViewController, retrieving the popoverPresentationController and then setting the popoverPresentationController's delegate to itself (to InitialViewController). You can see the result, below:
For maximum reusability, however, it would be great if the InitialViewController did not need to know anything about how the presentation controller is delegated. I think it should be possible for the PopoverViewController to set itself as the popoverPresentationController's delegate. I've tried this in either the viewDidLoad or the viewWillAppear functions of the PopoverViewController. However, the PopoverViewController is presented modally in both cases, as shown below:
All the code is contained in just the InitialViewController and the PopoverViewController. The code used in the failing version of the InitialViewController is shown below:
import UIKit
// MARK: - UIViewController subclass
class InitialViewController: UIViewController {
struct Lets {
static let storyboardName = "Main"
static let popoverStoryboardID = "Popover View Controller"
}
#IBAction func showPopoverButton(_ sender: UIButton) {
// instantiate & present the popover view controller
let storyboard = UIStoryboard(name: Lets.storyboardName,
bundle: nil )
let popoverViewController =
storyboard.instantiateViewController(withIdentifier: Lets.popoverStoryboardID )
popoverViewController.modalPresentationStyle = .popover
guard let popoverPresenter = popoverViewController.popoverPresentationController
else {
fatalError( "could not retrieve a pointer to the 'popoverPresentationController' property of popoverViewController")
}
present(popoverViewController,
animated: true,
completion: nil )
// Retrieve and configure UIPopoverPresentationController
// after presentation (per
// https://developer.apple.com/documentation/uikit/uipopoverpresentationcontroller)
popoverPresenter.permittedArrowDirections = .any
let button = sender
popoverPresenter.sourceView = button
popoverPresenter.sourceRect = button.bounds
}
}
The code in the failing PopoverViewController is shown below:
import UIKit
// MARK: - main UIViewController subclass
class PopoverViewController: UIViewController {
// MARK: API
var factorForMarginsAroundButton: CGFloat = 1.2
// MARK: outlets and actions
#IBOutlet weak var popoverLabel: UILabel!
// MARK: lifecycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear( animated )
// set the preferred size for popover presentations
let labelSize =
popoverLabel.systemLayoutSizeFitting( UILayoutFittingCompressedSize )
let labelWithMargins =
CGSize(width: labelSize.width * factorForMarginsAroundButton,
height: labelSize.height * factorForMarginsAroundButton )
preferredContentSize = labelWithMargins
// set the delegate for the popoverPresentationController to self
popoverPresentationController?.delegate = self
}
}
// MARK: - UIPopoverPresentationControllerDelegate
// (inherits from protocol UIAdaptivePresentationControllerDelegate)
extension PopoverViewController: UIPopoverPresentationControllerDelegate
{
func adaptivePresentationStyle(for controller: UIPresentationController,
traitCollection: UITraitCollection)
-> UIModalPresentationStyle{
return .none
}
}
Is it possible for a view controller that is being presented as a popover to be the delegate for its own popoverPresentationController?
I'm using Xcode 8.0, Swift 3.1 and the target is iOS 10.0
It's certainly possible. You're dealing with a timing issue. You need to set the delegate before viewWillAppear. Unfortunately, there is no convenient view lifecycle function to insert the assignment into, so I did this instead.
In your PopoverViewController class, assign the delegate in an overriden getter. You can make the assignment conditional if you'd like. This creates a permanent relationship, so other code code never "override" the delegate by assigning it.
override var popoverPresentationController: UIPopoverPresentationController? {
get {
let ppc = super.popoverPresentationController
ppc?.delegate = self
return ppc
}
}
As #allenh has correctly observed, you need to set the delgate before viewWillAppear, and he has offered a clever solution by setting the delegate by overriding the popoverPresentationController getter.
You could also set the delegate to the popover itself in your showPopover() function between setting modalPresentationStyle and presenting the popover:
let vc = storyboard.instantiateViewController(withIdentifier: Lets.popoverStoryboardID )
vc.modalPresentationStyle = .popover
vc.popoverPresentationController?.delegate = vc
present(vc, animated: true, completion: nil)

Removing Data on Maps with different view controllers

I am really struggling on an issue that I think is rather interesting and quite difficult. My application lets the user create annotation locations within a Mapview. They also have the option to edit and delete these locations in another modal view controller.
The issue I am facing is that when the user presses delete, which removes the location from firebase, the annotation is still displayed upon the map. I cannot reload my annotation data within the view did appear as this does not suit my application. I cant have my annotations being reloaded every time I bring up the Mapview.
I need to figure out a way to implement an annotation reload when the delete button is pressed. However, as this happens within my delete view controller (which does not contain the mapView) I cannot use the reload function. Is there a way to connect view controllers so that I can apply the reload function when delete is pressed?
Updated Code **
This is my map view controller:
class ViewController: UIViewController, SideBarDelegate, MGLMapViewDelegate, DeleteVCDelegate {
let EditSaveSpotController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "EditVC") as! EditSaveSpotViewController
override func viewDidLoad() {
super.viewDidLoad()
EditSaveSpotController.delegate = self
}
func wholeRefresh() {
let uid = FIRAuth.auth()!.currentUser!.uid
let userLocationsRef = FIRDatabase.database().reference(withPath: "users/\(uid)/personalLocations")
userLocationsRef.observe(.value, with: { snapshot in
for item in snapshot.children {
guard let snapshot = item as? FIRDataSnapshot else { continue }
let newSkatepark = Skatepark(snapshot: snapshot)
self.skateparks.append(newSkatepark)
self.addAnnotation(park: newSkatepark)
}
})
if let annotations = mapView.annotations {
mapView.removeAnnotations(annotations)
}
for item in skateparks {
self.addAnnotation(park: item)
}
}
This is my delete view controller:
import UIKit
import Firebase
protocol DeleteVCDelegate {
func wholeRefresh()
}
class EditSaveSpotViewController: UIViewController {
var delegate: DeleteVCDelegate?
#IBAction func deleteSkateSpot(_ sender: Any) {
ref = FIRDatabase.database().reference(withPath: "users").child(Api.User.CURRENT_USER!.uid).child("personalLocations/\(parkId!)")
ref.observe(.value, with: { (snapshot) in
self.ref.setValue(nil)
self.dismiss(animated: true, completion: nil)
self.delegate?.wholeRefresh()
// self.delegate?.mainRefresh()
print("CheckWorking")
})
}
}
This is very high level and I did not have a chance to verify but it should be enough to get you going:
Modal Delete View
protocol DeleteVCDelegate {
func mainRefresh()
}
class DeleteVC: UIViewController {
var delegate: DeleteVCDelegate?
//your delete code
#IBAction func deleteSkateSpot(_ sender: Any) {
ref = FIRDatabase.database().reference(withPath: "users").child(Api.User.CURRENT_USER!.uid).child("personalLocations/\(parkId!)")
ref.observe(.value, with: { (snapshot) in
self.ref.setValue(nil)
//call to delegate
self.delegate?.mainRefresh()
})
}
}
MapView Class (implement DeleteVCDelegate)
class mapVC: MKMapViewDelegate, DeleteVCDelegate{
//when you present your DeleteVC set its delegate to the map view
let vc=(self.storyboard?.instantiateViewController(withIdentifier: "deleteVC"))! as! DeleteVC
//set the delegate
vc.delegate=self
//present deleteVC
self.present(vc, animated: true, completion:nil)
//implement delegate method of DeleteVC
func mainRefresh(){
//dismiss modal
self.dismiss(animated: true) {
//update view
self.loadLocations()
self.annotationRefresh()
}
}
}

What is the correct way to close a popover?

In my NSDocument subclass I instantiate an NSPopover, with .semitransient behaviour, and show it:
popover.show(relativeTo: rect, of: sender, preferredEdge: .maxX)
popover is declared locally. A button method in the popover controller calls:
view.window?.close()
The popover closes, but I have become aware that it remains in memory, deinit() is never called and the NSApp.windows count increases, whereas if I dismiss it by pressing escape or clicking outside it, deinit is called and the windows count doesn't increase.
If I set the window's .isReleasedWhenClosed to true, the windows count doesn't increase, but deinit is still not called.
(Swift 3, Xcode 8)
You have to call performClose (or close) on the popover, not the window.
Thanks -DrummerB for your interest. It has taken me some time to get around to making a simple test application I might send you, and of course it wasn't a document-based one as mine was, and that seemed to be clouding the issue. My way of opening the popover was based on an example I'd recently read, but can't now find or I'd warn people. It went like this:
let popover = NSPopover
let controller = MyPopover(...)! // my convenience init for NSViewController descendant
popover.controller = controller
popover.behaviour = .semitransient // and setting other properties
popover.show(relativeTo: rect, of: sender, preferredEdge: .maxX)
Here's the improved way I've come across:
let controller = MyPopover(...)! // descendant of NSViewController
controller.presentViewController(controller,
asPopoverRelativeTo: rect, of: sender, preferredEdge: .maxX,
behavior: .semitransient) // sender was a NSTable
In the view controller, the 'Done' button's action simply does:
dismissViewController(self)
which never worked before. And now I find the app's windows list doesn't grow, and the controller's deinit happens reliably.
I would suggest doing the following:
Define a protocol like this
protocol PopoverManager {
func dismissPopover(_ sender: Any)
}
In your popoverViewController (in this example we are displaying a filter view controller as a popover) add a variable for the popoverManager like this
/// Filter shown as a NSPopover()
class FilterViewController: NSViewController {
// Delegate
var popoverManager: PopoverManager?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
// Bind this to the close button or action on your popover view controller
#IBAction func closeAction(_ sender: Any) {
self.popoverManager?.dismissPopover(sender)
}
...
}
Now in your viewController that you show the popover from add an extension like this
extension MainViewController: NSPopoverDelegate, PopoverManager {
#IBAction func setFilter(_ sender: AnyObject) {
self.showFilterPopover(sender)
}
func showFilterPopover(_ sender: AnyObject) {
let storyboard = NSStoryboard(name: "Filter", bundle: nil)
guard let controller = storyboard.instantiateController(withIdentifier: "FilterViewController") as? FilterViewController else {
return
}
// Set the delegate to self so we can dismiss the popover from the popover view controller itself.
controller.popoverManager = self
self.popover = NSPopover()
self.popover.delegate = self
self.popover.contentViewController = controller
self.popover.contentSize = controller.view.frame.size
self.popover.behavior = .applicationDefined
self.popover.animates = true
self.popover.show(relativeTo: sender.bounds, of: sender as! NSView, preferredEdge: NSRectEdge.maxY)
}
func dismissPopover(_ sender: Any) {
self.popover?.performClose(sender)
// If you don't want to reuse it
self.popover = nil
}
}