Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use as callout view in MKMapView #175

Closed
dimovskidamjan opened this issue Feb 8, 2019 · 29 comments
Closed

Use as callout view in MKMapView #175

dimovskidamjan opened this issue Feb 8, 2019 · 29 comments

Comments

@dimovskidamjan
Copy link

I tried presenting the poptip when a user taps on a MKAnnotationView in a MKMapView, to act as a custom callout view like so:

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
_callout.show(attributedText: someAttributedText),
                          direction: .up,
                          maxWidth: 233,
                          in: mapView,
                          from: view.frame)
}

but the bubble is not attached to the annotation view, and it doesn't move with it when the map is panned but just stays in one place. Is there a way to achieve this?

@andreamazz
Copy link
Owner

The code seems fine, have you inspected the view hierarchy? Where is the tip placed?

@dimovskidamjan
Copy link
Author

Hi @andreamazz, the tip placement looks perfect, it's only when the map is panned that the original pin moves away and the tip remains where it was, i.e. it doesn't pan with the map. In the above chunk of code bear in mind that the "view" parameter is the MKAnnotationView, not the VC's main UIView (I don't think that's the issue, I just wanted to clarify).

@andreamazz
Copy link
Owner

Ok, I see, when you pan around the map view handles the annotations separately, if the tip is presented in the map view the tip will stay there. You should present the tip inside the annotationview itself. Make sure to chenge the from accordingly

@dimovskidamjan
Copy link
Author

Well, my map annotation is a simple UIBezierPath circle layer, and is no larger than 16x16, so I doubt the tip will fit in there. I tried setting the in parameter to the annotationView, but the tip appears way off, somewhere near the bottom of the screen. What would you recommend?

@andreamazz
Copy link
Owner

Add it to the annoationView, turn off clipping if it's too small, and adjust the from frame (origin in the middle of the annoationview's frame, and size 1px by 1px).
Consider that calling show on a poptip it's kinda like calling in.addSubview(popTip)

@dimovskidamjan
Copy link
Author

dimovskidamjan commented Feb 8, 2019

I tried:

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
view.clipsToBounds = false
_callout.show(attributedText: someAttributedText),
                          direction: .up,
                          maxWidth: 233,
                          in: view,
                          from: CGRect(x: view.bounds.midX, y: 0, width: 1, height: 1))
}

img_fde9886c1384-1

As you can see, something weird is happening this way, even the arrow is a bit off to the right for some reason. The tip stays with the annotation when the map moves around, so that part is fine, but the positioning is not right.

@andreamazz
Copy link
Owner

Mh, it might be trying to constraint the bubble inside the tiny view. 🤔
I should make that feature optional. In the meantime, can you expand the annotationview making the view bigger, while keeping the red dot in the middle?
If that fixes the issue I can add a flag to disable the constraining.

@dimovskidamjan
Copy link
Author

If I make the frame of the annotationView big enough to fit the tip then the frames of the different pins start overlapping and tapping one pin opens up another one's callout. Even if I manage to fix this somehow, it will be a hacky solution and it's best if you can patch it up on your side if you have any ideas how to do it.

@dimovskidamjan
Copy link
Author

What's weird is that the little point triangle is always positioned correctly in relation to the pin, it's just the bubble that goes off when the frame is too small.

@andreamazz
Copy link
Owner

f I make the frame of the annotationView big enough to fit the tip then the frames of the different pins start overlapping and tapping one pin opens up another one's callout.

Yeah I mean to test this out as a temporary way to determine if my assumption is correct. If by enlarging the pin the poptip shows and moves fine I'll go on with the fix that I have in mind.

@dimovskidamjan
Copy link
Author

f I make the frame of the annotationView big enough to fit the tip then the frames of the different pins start overlapping and tapping one pin opens up another one's callout.

Yeah I mean to test this out as a temporary way to determine if my assumption is correct. If by enlarging the pin the poptip shows and moves fine I'll go on with the fix that I have in mind.

It does.

@andreamazz
Copy link
Owner

Ok, gotcha, I'll see if I can push a quick fix by making the constraint optional

andreamazz added a commit that referenced this issue Feb 8, 2019
@andreamazz
Copy link
Owner

I've attempted a blind fix in bbfe443
Can yo test it out?

@dimovskidamjan
Copy link
Author

Hey @andreamazz, sure I can, just give me some info about how I should do it.

@andreamazz
Copy link
Owner

If you use CococaPods just use

pod 'AMPopTip', github: 'https://github.com/andreamazz/AMPopTip'

Set the constrainInContainerView property to false

@dimovskidamjan
Copy link
Author

@andreamazz this is what I have now:

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
view.clipsToBounds = false
_callout.isRounded = true
_callout.constrainInContainerView = false
_callout.show(attributedText: someAttributedText),
                          direction: .up,
                          maxWidth: 233,
                          in: view,
                          from: CGRect(x: view.bounds.midX, y: 0, width: 1, height: 1))
}

and here is the result:

screen shot 2019-02-11 at 10 13 08 am

It's better than before because I didn't have to do any work on the original pin's frame in order to get this to work, but it still seems off a bit. I'm guessing maybe the rounded corners are an issue?

@andreamazz
Copy link
Owner

Nah, it's still trying to constraint the view inside the pin.
It's hard o debug without a working sample :/
If you can share a sample project with the basic setup that'd be great, otherwise I'll see if I can concoct something.

@dimovskidamjan
Copy link
Author

Sure, how would you like me to share it with you?

@andreamazz
Copy link
Owner

Create a quick repo if it can be public, or just send an email at andrea @fancypixel.it with the zipped repo

@dimovskidamjan
Copy link
Author

I've sent you an email with the demo project.

@andreamazz
Copy link
Owner

Great, thanks, I'll look into it

@dimovskidamjan
Copy link
Author

@andreamazz thanks! This may help you, from what I can see the issue is dual:

  1. The tip bubble is not centered above the pin (might be because of the way I'm presenting it - see code in previous comments)
  2. The tip arrow becomes detached from the tip bubble when the corners are completely round, it seems perfectly fine when the corners are square or with a smaller radius.

@andreamazz
Copy link
Owner

Ok, checkout the latest commit, it fixes the issue in your sample:
screenshot 2019-02-12 at 09 28 21

@dimovskidamjan
Copy link
Author

The issue seems to be resolved now. Thanks! Will you be pushing this to the main branch soon?

@dimovskidamjan
Copy link
Author

@andreamazz This seems to disable the tapHandler, I think it's not getting called now. Could you please check?

@dimovskidamjan
Copy link
Author

@andreamazz Any new info on this? Can I help or contribute somehow?

@andreamazz
Copy link
Owner

Sorry, I'm a little swamped at work in these days.
Sure, feel free to open a PR with a proposed fix if you have the time.

@dimovskidamjan
Copy link
Author

I did a little testing, and here's what I've found:

In the demo app, I modified the "attributed text" pop tip case like so

popTip.constrainInContainerView = false
sender.clipsToBounds = false
popTip.show(attributedText: attributedText, direction: .up, maxWidth: 200, in: sender, from: sender.bounds)

to achieve the same scenario as I'm using it in the map. This caused the view to go a bit out of bounds of the screen, but that's not a problem for me. However the tap of the view is no longer working, as I previously reported.

As explained in the Apple docs, the tap gesture recognizer only works on subviews which are not clipped by the bounds of their superview, which is exactly the case here. In order to get this to work, what I needed to do was make a custom class for my view which holds the pop tip, and override the hitTest method in there to make it recognize taps outside its bounds. I'm sharing my code below for future reference:

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    let reversedSubviews = subviews.reversed()
    let hitSubview = reversedSubviews
        .first(where: { $0.hitTest(convert(point, to: $0), with: event) != nil })
    if hitSubview != nil {
        return hitSubview
    }

    return super.hitTest(point, with: event)
}

I don't see how this could be fixed by your library, and I don't think it's even your job to find a solution for this, but if you have something in mind then feel free to include it and make it even better for future users.

As far as I am concerned, you can consider this issue resolved. Thanks for all your help!

@andreamazz
Copy link
Owner

Sounds good. I’ll release a new version on CocoaPods as soon as possible.
Cheers

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants