Google Places Autocomplete Delegate not set - swift

I am setting the delegate of the autocomplete view controller to the current class, which has an autocompleteviewcontrollerdelegate extension. This has has always worked in the past, but recently I am getting an error message printing out that says the delegate for the autocomplete controller is not set and I can confirm the delegate is no longer being called. I have the print(self) line to check that I am in fact setting the correct class to be the delegate. I have this exact same setup in another part of my app that is working fine. Any suggestions on how to debug this issue?
#objc func searchButton() {
print(self)
let acController = GMSAutocompleteViewController()
acController.delegate = self
acController.autocompleteBounds = getBounds(latitude: latitude!, longitude: longitude!)
// Specify a filter.
//let filter = GMSAutocompleteFilter()
//filter.type = establishment
//acController.autocompleteFilter = filter
present(acController, animated: true, completion: nil)
}

In my viewwillDisappear method I pop the view controller so that if the user navigated away from the page with the tab bar controller the next time they navigate to the page they will be on the main page. Whenever the autocomplete view controller was appearing the parent viewcontroller was getting popped off the stack and killing off the delegate.

Related

swift xcode iOS: can I re-use a loaded modal fullscreen view controller?

I have a storyboard with two view controllers. First one, VC_1, has one button that opens 2nd one - VC_2.
VC_2 also has a button that opens VC_1.
Both controllers have almost identical code:
class VC_1: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
print(“VC_1 loaded")
}
override func viewDidAppear(_ animated: Bool){ print(“VC_1 appeared") }
override func viewDidDisappear(_ animated: Bool){ print(“VC_1 disappeared") }
#IBAction func btnShowVC_2(_ sender: UIButton)
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
secondVC = storyboard.instantiateViewController(identifier: “VC_2”)
secondVC.modalPresentationStyle = .fullScreen
show(secondVC, sender: self)
}
}
The difference is only in "VC_2" instead of "VC_1" in the 2nd controller code.
I have seen this View Controller creation code in Apple documentation and many other examples around the Internet.
When I press the button on the VC_1, I see in the debug window, that VC_2 is loaded and appeared, and VC_1 is disappeared. And same, of course, happens when I press the button on VC_2 - it disappears, and VC_1 is loaded again.
My questions are:
what happens with View Controller object after "viewDidDisappear" has been called? Does it really disappear from memory, or "disappear" only means "you cannot see it on the screen?". I do not see "viewDidUnload" in the documentation...
I suppose that "viewDidLoad" means that new View Controller object was created in memory. Is there any way to load the View Controller object only once, and then hide and show it without causing "viewDidLoad" to be called? I tried to do it with global variable "secondVC" but got "Application tried to present modally an active controller" error.
viewDidDisappear: called after the view is removed from the windows’
view hierarchy. No, View controller object just left the view property. By the way the amount of memory used by view controllers is negligible. So dont think about too much. If you want to catch when Your View controller object release from the memory put
deinit { print("vc deallocated") }
viewDidUnload, it has been deprecated since the iOS
6, which used to do some final cleaning.
Partly true. Keep in mind ViewDidload called one time for the life cycle of view controller. There is a method called before viewdidload but this is not related with your question.
In addition to "There is a method before viewdidload" -> loadView( ) is a method managed by the viewController. The viewController calls it when its current view is nil. loadView( ) basically takes a view (that you create) and sets it to the viewController’s view (superview).

Swift macOS SegmentedControl Action not getting called

Description
I am trying to use NSSegmentedControls to transition between Child ViewControllers. The ParentViewController is located in Main.storyboard and the ChildViewControllers are located in Assistant.storyboard. Each ChildViewController has a SegmentedControl divided into 2 Segments and their primary use is to navigate between the ChildViewControllers. So they are set up as momentaryPushIn rather than selectOne. Each ChildViewController uses a Delegate to communicate with the ParentViewController.
So in the ParentViewController I added the ChildViewControllers as following:
/// The View of the ParentViewController configured as NSVisualEffectView
#IBOutlet var visualEffectView: NSVisualEffectView!
var assistantChilds: [NSViewController] {
get { return [NSViewController]() }
set(newValue) {
for child in newValue { self.addChild(child) }
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
addAssistantViewControllersToChildrenArray()
}
override func viewWillAppear() {
visualEffectView.addSubview(self.children[0].view)
self.children[0].view.frame = self.view.bounds
}
private func addAssistantViewControllersToChildrenArray() -> Void {
let storyboard = NSStoryboard.init(name: "Assistant", bundle: nil)
let exampleChild = storyboard.instantiateController(withIdentifier: "ExampleChild") as! ExampleChildViewController
let exampleSibling = storyboard.instantiateController(withIdentifier: "ExampleSibling") as! ExampleSiblingViewController
exampleChild.navigationDelegate = self
exampleSibling.navigationDelegate = self
assistantChilds = [exampleChild, exampleSibling]
}
So far so good. The ExampleChildViewController has an NSTextField instance. While I am in the scope of the TextField, I can trigger the action of the SegmentedControls. Its navigating forward and backward as it should. But once I leave the scope of the TextField I can still click the Segments, but they are not triggering any action. They should be able to navigate forward and backward even if the TextField is not the current "First Responder" of the application. I think I am missing something out here, I hope anyone can help me with this. I know the problem is not the NSSegmentedControl because I am seeing the same behavior with an NSButton, which is configured as Switch/Checkbox, in the SiblingViewController. I just don't have any idea anymore what I am doing wrong.
It`s my first time asking a question myself here, so I hope the way I am doing is fine for making progress with the solution. Let me know if I can do something better/different or if I need to provide more information about something.
Thanks in advance!
Additional Information
For the sake of completeness:
The ParentViewController itself is embedded in a ContainerView,
which is owned by the RootViewController. I can't imagine this does
matter in any way, but this way we are not missing something out.
I am actually not showing the navigation action, because I want to
keep it as simple as possible. Furthermore the action is not problem,
it does what I want it to do. Correct me if I am wrong with this.
Possible solutions I found while researching, which did not work for me:
Setting window.delegate of the ChildViewControllers to NSApp.windows.first?.delegate
Setting the ChildViewController to becomeFirstResponder in its func viewWillAppear()
visualEffectView.addSubview(self.children[0].view, positioned: NSWindow.OrderingMode.above, relativeTo: nil)
Related problems/topics I found while researching:
Basic segmented control not working
Adding and Removing Child View Controllers
NSSegmentedControl - Odd appearance when placed in blur view
How to set first responder to NSTextView in Swift?
How to use #selector in Swift 2.2 for the first responder
Accessing methods, actions and/or outlets from other controllers with swift
How to use Child View Controllers in Swift 4.0 programmatically
Container View Controllers
issues with container view
Control a NSTabViewController from parent View
How to detect when NSTextField has the focus or is it`s content selected cocoa
SOLUTION
let parentViewControllerInstance = self.parent as! ParentViewController
segmentedControl.target = parentViewControllerInstance
In my case I just had to set the delegate as the target of the sendAction method.
Background
Ok, after hours of reading the AppKit Documentation I am now able to answer my own question.
First, debugging the UI showed that the problem was definitely not in the ViewHierarchy.
So I tried to think about the nature of NSButton and NSSegmentedControl. At some point I noticed that both are subclasses of NSControl.
class NSSegmentedControl : NSControl
class NSButton : NSControl
The AppKit Documentation says:
Discussion
Buttons are a standard control used to initiate actions within your app. You can configure buttons with many different visual styles, but the behavior is the same. When clicked, a button calls the action method of its associated target object. (...) You use the action method to perform your app-specific tasks.
The bold text points to the key of the solution – of its associated target object. Typically I define the action of an control item like this:
button.action = #selector(someFunc(_:))
This causes the NSControl instance to call this:
func sendAction(_ action: Selector?, to target: Any?) -> Bool
Parameter Description from the documentation:
Parameters
theAction
The selector to invoke on the target. If the selector is NULL, no message is sent.
theTarget
The target object to receive the message. If the object is nil, the application searches the responder chain for an object capable of handling the message. For more information on dispatching actions, see the class description for NSActionCell.
In conclusion the NSControl instance, which was firing the action method (in my case the NSSegmentedControl), had no target to send its action to. So it was only able to send its action method across the responder chain - which obviously has been nil while the first responder was located in another view.

Opening window + view from an other view controller

I've got a ViewControllerOne. ViewControllerOne is connected via Ctrl-Drag (in storyboard) to a menu-button mBtn (which means I don't know how it is implemented programmatically).
Clicking on this mBtn, a ViewOne appears (present modal). This ViewOne is bound to ViewControllerOne. ViewOne has a button btnOne.
Clicking on btnOne I want ViewOne to be dismissed and ViewTwo to be shown. ViewTwo belongs to ViewControllerTwo and to WindowControllerTwo.
The WindowControllerTwo-ViewControllerTwo-binding is the standard case as created on a new project.
I have the following code in the IBAction for button btnOne in ViewControllerOne:
#IBAction func onbtnOnePressed(sender: AnyObject){
let m_WindowControllerTwo = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("WindowControllerTwo")) as! NSWindowController // I have no custom class for the window controller as I don't really know what I can use it for ...
let m_ViewTwo = WindowControllerTwo.contentViewController as! ViewControllerTwo // my custom class for ViewTwo
m_ViewTwo.attributeIWantToPassToThisView = self.x // an attribute I want to pass from view a to view b
m_WindowControllerTwo.contentViewController = m_ViewTwo // passing the attribute from a to b
m_WindowControllerTwo.showWindow(self) // this does not work
self.dismiss(nil) // see NOTE
}
This code actually does not work. On debugging it step by step, I'm seeing the window/view flickering but not appearing...
NOTE: I could connect the button btnOne with a ctrl-drag to ViewControllerTwo. This works. But then the current ViewOne does not get dismissed!
Question: What am I doing wrong here? In iOS swift this also works. I don't quite get the WindowController stuff, so I'll need your advice on this.
Instead of this: m_WindowControllerTwo.showWindow(self)
use:
let application = NSApplication.shared()
application.runModal(for: wordCountWindow) //this will present WindowControllerTwo modally.
then to close your present controller add this line: PresentWindowControllerName.close()

How do I segueing to the UIViewController I'm in?

I'm trying to go from one view, filled with data from an object, to the same view but filled with a different object via segue.
Using a segue is necessary as apposed to switching the object and refreshing the view because my users need to be able to go back to the past view controller when they hit the back button.
Example:
(ThisView, with thisObject populating the view) -> (ThisView, with thisOtherObject populating the view)
What I've tried:
presentView Controller: This didn't work because it is not the default segue I'm trying to achieve.
let next: NewClubProfile = storyboard?.instantiateViewControllerWithIdentifier("clubProfile") as! NewClubProfile
presentViewController(next, animated: true, completion: nil)
Using Navigation Controller: Can't figure out how to segue the new object to populate the other view
let next: NewClubProfile = storyboard?.instantiateViewControllerWithIdentifier("clubProfile") as! NewClubProfile
self.navigationController?.pushViewController(next, animated:true)
Should have taken longer to solve this myself before posting this but I couldn't find an answer that fit what I needed. Anyway, solved it, here's what worked for me.
let next: ViewController = storyboard?.instantiateViewControllerWithIdentifier("viewName") as! ViewController
next.object = newObject
self.navigationController?.pushViewController(next, animated:true)

Warning: Attempt to present view controller on another view controller whose view is not in the window hierarchy

I have a working simple single player game, where the initial view controller has a button to start the game. This button performs a segue and all game logic in the GameViewController is working as expected.
I've followed this tutorial to add multi player functionality to my game.
On the initial view controller, a button now calls
GameKitHelper.sharedGameKitHelper.findMatchWithMinPlayers(2, maxPlayers: 2, viewController: self, delegate: MultiPlayerNetworking)
}
which has the following implementation in GameKitHelper.swift:
func findMatchWithMinPlayers (minPlayers: Int, maxPlayers: Int, viewController: UIViewController, delegate: GameKitHelperDelegate) {
matchStarted = false
let request = GKMatchRequest()
self.delegate = delegate
request.minPlayers = 2
request.maxPlayers = 2
presentingViewController = viewController
presentingViewController.dismissViewControllerAnimated(false, completion: nil)
let mmvc = GKMatchmakerViewController(matchRequest: request)
mmvc?.matchmakerDelegate = self
presentingViewController.presentViewController(mmvc!, animated: true, completion: nil)
self.delegate?.matchStarted()
}
The Class MultiPlayerNetworking implements the GameKitHelper protocol, and gets called on the matchStarted function.
The MultiPlayerNetworking class in essence takes over here, and starts sending out messages to hosts and remote players.
Note that some time later, When auto-matching finishes, the following function gets called in GameKitHelper:
func matchmakerViewController(viewController: GKMatchmakerViewController, didFindMatch match: GKMatch) {
viewcontroller.dismissViewControllerAnimated(true, completion: {})
self.match = match
match.delegate = self
}
Now, I think this says that the GKMatchmakerViewController is dismissed, thereby showing me the initial view controller again (and this is what happens on screen).
Now my issue! After the GKMatchmakerViewController is dismissed, I'm back at the initial view controller and want to 'simulate' an automatic segue to my gameView (which has logic to deal with a multi player game as well).
I've made the initial view controller conform to the MultiPlayerNetworking protocol, which has a function to simulate a segue:
func segueToGVC() {
self.performSegueWithIdentifier("game", sender: nil) // self = initial view controller
}
However, xCode complains with:
Warning: Attempt to present <GameViewController: 0x7d440050> on <GKMatchmakerViewController: 0x7c8fbc00> whose view is not in the window hierarchy!
I'm stuck here, and have tried so many different methods of dismissing the view controller, to making sure I'm calling the performSegue function on the topViewController via this link, but nothing works.
My question: why is the GKMatchmakerViewController visually dismissed, but still present in the view hierarchy, such that calling a performSegue function on the initial view controller give the above error/warning?
Views are greatly appreciated!
why is the GKMatchmakerViewController visually dismissed, but still present in the view hierarchy
Here are two suggestions:
Perhaps it's because dismissal takes time. You are saying:
viewcontroller.dismissViewControllerAnimated(true, completion: {})
So there's an animation. Don't attempt to perform the next segue until the animation is over.
Perhaps you are just wrong about who self is. You are saying:
self.performSegueWithIdentifier("game", sender: nil)
// self = initial view controller
We have only your word, in that comment, for who self is. Meanwhile, the runtime seems to think differently about the matter:
Attempt to present <GameViewController: 0x7d440050> on <GKMatchmakerViewController: 0x7c8fbc00>
It might be good to believe the runtime; after all, it knows more than you do.