Cant interact with custom infowindow in google map - ios

I have a google map and I've created a custom info window for my markers. I've put a couple buttons on the window, but I can't interact with them for the life of me. Here is my custom view:
class MarkerWindowView: UIView, UIGestureRecognizerDelegate {
//#IBOutlet weak var thumbNail: UIImageView!
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var videoLocation: UILabel!
#IBOutlet weak var tags: UILabel!
override func awakeFromNib() {
println("awake From Nib")
self.userInteractionEnabled = true
print(self.userInteractionEnabled)
/*let recognizer = UITapGestureRecognizer(target: self, action:Selector("handleTap:"))
recognizer.delegate = self
/*for test in layer.sublayers {
println(test)
}*/*/
}
#IBAction func playVideoPressed(sender: AnyObject) {
println("play video")
}
#IBAction func markerWindowViewWasTapped(sender: UITapGestureRecognizer) {
println("playvideo")
}
/*func handleTap(recognizer: UITapGestureRecognizer) {
println("tapped")
}*/
}
and here is the method that returns the view in the view controller:
func mapView(mapView: GMSMapView!, markerInfoWindow marker: GMSMarker!) -> UIView! {
println("markeInfoWindow")
var newView = NSBundle.mainBundle().loadNibNamed("MarkerWindowView", owner: self, options: nil).first! as! MarkerWindowView
//println(newView.description)
newView.userName.text = "Kazuma"
newView.videoLocation.text = "Harvard"
newView.tags.text = "Kazuma is crazy"
//view.bringSubviewToFront(newView)
//view.insertSubview(newView, atIndex: 0)
//newView.userInteractionEnabled = true
/*for subView in self.view.subviews {
println(subView)
}*/
//view.bringSubviewToFront(newView)
return newView
}
I have tried a bunch of stuff to try to interact with this view including shifting views in the stack, moving layers etc. Has anyone had this problem before? Any idea what I'm doing wrong. The stuff I commented out is mostly me trying to hack it.

A custom info window is rendered as an UIImage, so you cannot interact with anything on it. I had a similar problem, and ended up making the entire window one "button" that caused an embed segue to push a view over the map, giving me details (think Yelp app style).
My info window was contained in a separate .xib. Here is some code in my GMSMapViewDelegate:
func mapView(mapView: GMSMapView!, didTapInfoWindowOfMarker marker: GMSMarker!)
{
ShowDetailView(mapMarkerManager!.GetMapMarker(marker)!)
}
func mapView(mapView: GMSMapView!, markerInfoWindow marker: GMSMarker!) -> UIView!
{
let markerInfoWindow = UINib(nibName: "MarkerInfoPopup", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! MarkerInfoPopup
markerInfoWindow.setValues(mapMarkerManager!.GetMapMarker(marker)!)
return markerInfoWindow
}
private func ShowDetailView(mapMarker: MapMarker)
{
delegate!.SetMapMarker(self, mapMarker: mapMarker)
self.view.bringSubviewToFront(mapDetailView)
mapDetailView.hidden = false
mapView.hidden = true
BackToMapButton.hidden = false
}
(mapMarker is more or less a GMSMarker wrapper. It does other stuff but is not important in this context)
I know this isn't exactly what you wanted, but unfortunately there aren't many options when using Google Maps' built in markerInfoWindow/didTapInfoWindowOfMarker.
Reference: https://developers.google.com/maps/documentation/ios-sdk/marker?hl=en#info_windows
"Note: The info window is rendered as an image each time it is displayed on the map. This means that any changes to its properties while it is active will not be immediately visible. The contents of the info window will be refreshed the next time that it is displayed."

Related

recognize which UIImageView has been tapped and get its index in an array

So right now I have code that makes a TapGestureRecognizer for each image in an array, and what I'm trying to do is return the index of the image that has been tapped by the user. What would be the easiest way to achieve this? I'm new to Swift so I got part of this from a youtube tutorial and I don't really understand how to use gesture recognizers :/
This is what I have so far (simplified to only include what is relevant):
import UIKit
class HomeViewController: UIViewController {
var recognizersAdded = false
#IBOutlet weak var right1: UIImageView!
#IBOutlet weak var right2: UIImageView!
#IBOutlet weak var right3: UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
var houseImages = [self.right1, self.right2, self.right3]
for imageview in houseImages
{
if !recognizersAdded
{
let recognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
imageview!.addGestureRecognizer(recognizer)
imageview!.isUserInteractionEnabled = true
}
}
recognizersAdded = true
}
#IBAction func imageTapped(recognizer: UIGestureRecognizer)
{
if let view = recognizer.view as? UIImageView
{
var i = 0
//set i to index of image tapped
advance4(index: i)
}
}
Since you already have an array storing all your images, it's fairly easy to know what image (and index) has been tapped by searching the image index within houseImages.
Because you don't need complex verifications to get your index with firstIndex(where: [...]), I would suggest to simply use firstIndex(of: UIImageView) in your case.
#IBAction func imageTapped(recognizer: UIGestureRecognizer) {
guard let tappedView = recognizer.view as? UIImageView else {
return
}
let index = self.houseImages.firstIndex(of: tappedView)
print("House image index = \(index)")
}
You can do this quite easily since you are already creating an array of UIImageView's and you just need the index of the tapped imageView within the array, you simply need to use the method firstIndex(where:, here is the code:
#IBAction func imageTapped(recognizer: UIGestureRecognizer) {
let houseImages = [self.right1, self.right2, self.right3]
if let view = recognizer.view as? UIImageView {
let indexOfSelectedImageView = houseImages.firstIndex(where: { $0 == view })
print(indexOfSelectedImageView)
}
}

How to create a map with custom annotation and region focus when mapView is scrolled or marker is tapped?

I want to implement a MapKit with an UIView that has the same effect of TripAdvisor and ClassPass app. Basically, I want to add a scrollable view in the bottom of the view and when the user scroll in the map, the bottom view with the marker's information changes with the new marker's details.
Examples of what I want to achieve:
https://uigarage.net/maps-on-ios-by-tripadvisor/
Second example with ClassPass app:
https://medium.com/classpass-engineering/creating-a-fluid-scroll-experience-on-ios-faeb29be3bdb
Third example, with the code in React Native:
https://codedaily.io/tutorials/9/Build-a-Map-with-Custom-Animated-Markers-and-Region-Focus-when-Content-is-Scrolled-in-React-Native
I was able to create the Apple map with the markers, and the UIView, as can be seen on the next pictures in the links below:
Storyboard's Screenshot
Simulator's Screenshot
However, I want that to change the UIView dynamically, based on the map region, or when tapping the marker. How can I reach that result?
Here's the current code:
#IBOutlet weak var reviewView2: DesignableView!
#IBOutlet weak var authorLabel: UILabel!
#IBOutlet weak var reviewLabel: UILabel!
#IBOutlet weak var Star1: UIImageView!
#IBOutlet weak var Star2: UIImageView!
#IBOutlet weak var Star3: UIImageView!
#IBOutlet weak var Star4: UIImageView!
#IBOutlet weak var Star5: UIImageView!
#IBOutlet weak var authorImageView: UIImageView!
#IBOutlet weak var reviewView: DesignableView!
var gpxURL: NSURL? {
didSet {
clearWaypoints()
if let url = gpxURL {
GPX.parse(url: url as URL) { gpx in // asynchronous
if gpx != nil {
self.addWayPoints(waypoints: gpx!.waypoints)
}
}
}
}
}
let urlstring = "Long_Lat"
override func viewDidLoad() {
super.viewDidLoad()
// gpxURL = URL(string: urlstring)
gpxURL = Bundle.main.url(forResource: urlstring, withExtension: "gpx") as NSURL?
reviewView.isHidden = true
reviewView2.isHidden = true
}
#IBAction func reviewViewsTapped(_ sender: UITapGestureRecognizer) {
print("Button tapped")
}
// MARK: - MKMapViewDelegate
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var view = mapView.dequeueReusableAnnotationView(withIdentifier: Constants.AnnotationViewReuseIdentifier)
if view == nil {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: Constants.AnnotationViewReuseIdentifier)
}else{
view?.annotation = annotation
}
view?.canShowCallout = false
guard !annotation.isKind(of: MKUserLocation.self) else {
//for the custom image on current location
view?.image = #imageLiteral(resourceName: "gift")
return view
}
view?.image = UIImage(named: "gift")
let scaleTransform = CGAffineTransform(scaleX: 0.0, y: 0.0) // Scale
UIView.animate(withDuration: 0.2, animations: {
view?.transform = scaleTransform
view?.layoutIfNeeded()
}) { (isCompleted) in
// Nested block of animation
UIView.animate(withDuration: 0.3, animations: {
view?.alpha = 1.0
view?.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
AnimationUtility.viewSlideInFromBottom(toTop: view!)
view?.layoutIfNeeded()
})
}
return view
}
Any idea on how to do that using Swift 5 and UIKit?
Thanks in advance
u can use google maps,
set your custom map markers it has all methods you can read more here
https://developers.google.com/maps/documentation/ios-sdk/intro

How to remove popView if we tap anywhere in the background except tapping in popView in swift

I have created one popView with textfield and button in ViewController. if i click button then popView is appearing, and i am able to enter text in textfield and submit is working, and if i tap anywhere in view also i am able to remove popView, but here i want if i tap on anywhere in popView i don't want to dismiss popView, Please help me in the code.
here is my code:
import UIKit
class PopUPViewController: UIViewController {
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
popView.isHidden = true
// Do any additional setup after loading the view.
}
#IBAction func butnAct(_ sender: Any) {
view?.backgroundColor = UIColor(white: 1, alpha: 0.9)
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView))
view.addGestureRecognizer(tap)
}
#objc func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}
}
In my code if i tap anywhere in the view popView is removing even if i tap on popView also its removing, i don't need that, if i tap on popView then popView need not to be remove.
Please help me in the code
You can override the touchesBegan method which is triggered when a new touch is detected in a view or window. By using this method you can check a specific view is touched or not.
Try like this
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if touch?.view != self.popView {
dismissView()
}
}
func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
It's not the way I would have architected this, but to get around the problem you face you need to adapt your dismissView method so that it only dismisses the view if the tap is outside the popView.
To do this modify your selector to include the sender (the UITapGestureRecogniser )as a parameter:
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView(_:)))
and then in your function accept that parameter and test whether the tap is inside your view, and if so don't dismiss the view:
#objc func dismissView(_ sender: UITapGestureRegognizer) {
let tapPoint = sender.location(in: self.popView)
if self.popView.point(inside: tapPoint, with: nil)) == false {
self.popView.isHidden = true
view?.backgroundColor = .white
}
}
Your Popup view is inside the parent view of viewcontroller that's why on tap of popview also your popview is getting hidden.
So to avoid just add a view in background and name it bgView or anything what you want and replace it with view. And it will work fine .
Code:
#IBOutlet weak var bgView: UIView!//Add this new outlet
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var submitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
popView.isHidden = true
}
#IBAction func butnAct(_ sender: Any) {
bgView.backgroundColor = UIColor(white: 1, alpha: 0.9)//change view to bgView[![enter image description here][1]][1]
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissView))
bgView.addGestureRecognizer(tap)//change view to bgView
}
#objc func dismissView() {
self.popView.isHidden = true
bgView.backgroundColor = .white//change view to bgView
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}

Embedding a UIScrollView inside of a UIPageViewController and disabling pageView swiping?

I have a UIView subclass (a graph) embedded inside of a UIScrollview inside of a UIViewController which is one of a UIPageViewControllers pages... I'm trying to disable scrolling of the UIPageViewController so that the user scan scroll to see the far left of the graph without paging back. How can I do this? Is a ScrollView even the correct tool for this job?
class HistoricalHealthDataViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var graphScrollView: UIScrollView!
#IBOutlet weak var hockeyTrackerGraphView: HockeyTrackerGraphView! {
didSet {
self.hockeyTrackerGraphView.graphableObjects = HFRGraphableObjects
}
}
var HFRGraphableObjects: [HockeyTrackerGraphableObject] = []
override func viewDidLoad() {
super.viewDidLoad()
graphScrollView.delegate = self
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let parentPageViewController = parent as? HistoricalPageViewController {
for gestureRecognizer in parentPageViewController.gestureRecognizers {
print("gestureRecognizer")
gestureRecognizer.isEnabled = false
}
}
}
}
You don't show the code for it, but I'm assuming you implement UIPageViewControllerDataSource protocol and somewhere you have the methods:
func pageViewController(UIPageViewController, viewControllerBefore: UIViewController) -> UIViewController? {
}
func pageViewController(UIPageViewController, viewControllerAfter: UIViewController) -> UIViewController? {
}
If you arrange for those methods to return nil while paging is disabled, the page view won't scroll to a different page.

Why the app crashed when using KVO for observe in page view controller ? and How to make the KVO to be effected?

I'm trying to use KVO to observe the update change when using drag in my page view controller's child content view controller's scrollView, but when the app launch, it crashed says:
"Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x7ff003d3f5b0 of class KVOPageVC.ContentViewController was deallocated while key value observers were still registered with it."
Here below is my code and screenshot:
APP SCREENSHOT
CODE
PageViewController.swift
import UIKit
class PageViewController: UIPageViewController {
var pageLabels: Array<String> = ["First Page", "Second Page", "Third Page"]
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
self.setViewControllers([contentViewForPage(0)], direction: .Forward, animated: true, completion: nil)
}
func contentViewForPage(index: Int) -> ContentViewController {
let contentVC = storyboard!.instantiateViewControllerWithIdentifier("ContentVC") as! ContentViewController
contentVC.pageIndex = index
contentVC.label = pageLabels[index]
return contentVC
}
}
extension PageViewController: UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if index == 0 || index == NSNotFound {
return nil
}
index -= 1
return contentViewForPage(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if index == NSNotFound {
return nil
}
index += 1
if index == self.pageLabels.count {
return nil
}
return contentViewForPage(index)
}
}
ObeserverViewController.swift
it's 'a view controller' embedded in 'content view controller's' 'Container View', when the user drag and release the scroll below, i want the emoji face to be replaced by the text "to be notified!"
import UIKit
class ObeserverViewController: UIViewController {
#IBOutlet weak var notifyLabel: UILabel!// when the user drag the scroll view and release, i hope its value will be changed accordingly.
var contentVC: ContentViewController! //the variable to hold the object
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.contentVC = self.storyboard!.instantiateViewControllerWithIdentifier("ContentVC")
self.contentVC.addObserver(self, forKeyPath: "changingLabel", options: [], context: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.contentVC.removeObserver(self, forKeyPath: "changingLabel")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if keyPath == "changingLabel" {
notifyLabel.text = "to be notified!"
}
}
deinit {
self.contentVC.removeObserver(self, forKeyPath: "changingLabel")
}
}
ContentViewController.swift
Page view controller's child view controllers, 3 pages in total. It includes a Scroll View and a Container View(embedded ObeserverViewController)
import UIKit
class ContentViewController: UIViewController {
var label: String!
var pageIndex: Int!
dynamic var changingLabel: String = ""
#IBOutlet weak var contentLabel: UILabel!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var contentView: UIView!
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
contentLabel.text = label
self.scrollView.delegate = self
self.contentView.backgroundColor = UIColor.greenColor()
}
}
extension ContentViewController: UIScrollViewDelegate {
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if scrollView.contentOffset.y < -50 {
if contentView.backgroundColor == UIColor.greenColor() {
contentView.backgroundColor = UIColor.yellowColor()
self.setValue("hah", forKey: "changingLabel")
} else {
contentView.backgroundColor = UIColor.greenColor()
self.setValue("wow", forKey: "changingLabel")
}
}
}
}
My questions is:
How can i make the emoji label text to be notified to change in one controller when i drag and release the scroll view in another controller?
Thank you very much in advance!
Firstly you are adding observer to one object and removing from another. When you are calling instanciateViewController... it will create you new object of passed view controller identifier. Than you are signing to it's changes via KVO. But in viewWillDissapear you are not getting the same object that was crated in viewWillAppear, but crating new one(it has nothing with that was created in viewWillAppear). Than you are resigning from it's notification, still as it is not the same object that you has created previously and signed to him(with KVO), such resigning won't lead to needed result. What you need to do is to save firstly created object to some variable and than resign this variable where it's needed.
Secondly you need to remove observer not only in viewWillDissapear method but as well in
deinit {
// perform the deinitialization
}
Than you will be sure that if your object is deleted than it as well will be resigned from notifications.
Your error message tells that object that was signed for notifications was marked as deleted, still it was not signed out and it will be receiving notification even if it's deleted(sound not really good as memory that he belongs to may already be used for some other objects, that may lead to undefined behavior of your app).

Resources