MKMapViewがドラッグされたかどうかを判断する方法はありますか?
ユーザーCLLocationCoordinate2D centre = [locationMap centerCoordinate];
がを使用してマップをドラッグするたびに中心位置を取得したいのですが、デリゲートメソッドなど、ユーザーがマップをナビゲートするとすぐに起動するものが必要です。
前もって感謝します
回答:
MKMapViewDelegateリファレンスを見てください。
具体的には、次の方法が役立つ場合があります。
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
これらのメソッドが呼び出されるように、マップビューのデリゲートプロパティが設定されていることを確認してください。
受け入れられた回答のコードは、何らかの理由でリージョンが変更されたときに発生します。マップドラッグを適切に検出するには、UIPanGestureRecognizerを追加する必要があります。ところで、これはドラッグジェスチャレコグナイザーです(パン=ドラッグ)。
手順1: viewDidLoadにジェスチャレコグナイザーを追加します。
-(void) viewDidLoad {
[super viewDidLoad];
UIPanGestureRecognizer* panRec = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didDragMap:)];
[panRec setDelegate:self];
[self.mapView addGestureRecognizer:panRec];
}
手順2:プロトコルUIGestureRecognizerDelegateをView Controllerに追加して、デリゲートとして機能するようにします。
@interface MapVC : UIViewController <UIGestureRecognizerDelegate, ...>
ステップ3: UIPanGestureRecognizerに次のコードを追加して、MKMapViewの既存のジェスチャレコグナイザーと連携させます。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
ステップ4:ドラッグごとに50回ではなく、1回メソッドを呼び出す場合は、セレクターで「ドラッグが終了した」状態を検出します。
- (void)didDragMap:(UIGestureRecognizer*)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
NSLog(@"drag ended");
}
}
UIPinchGestureRecognizer
、同様に追加する必要があります
readyForUpdate
そしてそのフラグをでチェックすること- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
です。
これは、ユーザーによって開始されたパンとズームの変更を検出する、私にとって有効な唯一の方法です。
- (BOOL)mapViewRegionDidChangeFromUserInteraction
{
UIView *view = self.mapView.subviews.firstObject;
// Look through gesture recognizers to determine whether this region change is from user interaction
for(UIGestureRecognizer *recognizer in view.gestureRecognizers) {
if(recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateEnded) {
return YES;
}
}
return NO;
}
static BOOL mapChangedFromUserInteraction = NO;
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
mapChangedFromUserInteraction = [self mapViewRegionDidChangeFromUserInteraction];
if (mapChangedFromUserInteraction) {
// user changed map region
}
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
if (mapChangedFromUserInteraction) {
// user changed map region
}
}
MKMapView
iOSの内部実装に依存していることに注意してください。この実装はAPIの一部ではないため、iOSのアップデートで変更される可能性があります。
(ちょうど)@mobiの優れたソリューションのSwiftバージョン:
private var mapChangedFromUserInteraction = false
private func mapViewRegionDidChangeFromUserInteraction() -> Bool {
let view = self.mapView.subviews[0]
// Look through gesture recognizers to determine whether this region change is from user interaction
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
if( recognizer.state == UIGestureRecognizerState.Began || recognizer.state == UIGestureRecognizerState.Ended ) {
return true
}
}
}
return false
}
func mapView(mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
mapChangedFromUserInteraction = mapViewRegionDidChangeFromUserInteraction()
if (mapChangedFromUserInteraction) {
// user changed map region
}
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
if (mapChangedFromUserInteraction) {
// user changed map region
}
}
self.mapView.subviews[0]
する必要がありましたself.mapView.subviews[0] as! UIView
self.map.delegate = self
、viewDidLoadに追加する必要がありました
上記のJanoの答えに対するSwift3ソリューション:
プロトコルUIGestureRecognizerDelegateをViewControllerに追加します
class MyViewController: UIViewController, UIGestureRecognizerDelegate
でUIPanGestureRecognizerを作成し、selfにviewDidLoad
設定delegate
します
viewDidLoad() {
// add pan gesture to detect when the map moves
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.didDragMap(_:)))
// make your class the delegate of the pan gesture
panGesture.delegate = self
// add the gesture to the mapView
mapView.addGestureRecognizer(panGesture)
}
プロトコルメソッドを追加して、ジェスチャレコグナイザが既存のMKMapViewジェスチャで動作するようにします
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
パンジェスチャでセレクターによって呼び出されるメソッドを追加します
func didDragMap(_ sender: UIGestureRecognizer) {
if sender.state == .ended {
// do something here
}
}
私の経験では、「入力中の検索」と同様に、タイマーが最も信頼できるソリューションであることがわかりました。パン、ピンチ、回転、タッピング、ダブルタッピングなどのジェスチャレコグナイザーを追加する必要がなくなります。
解決策は簡単です:
タイマーが作動したら、新しいリージョンのマーカーをロードします
import MapKit
class MyViewController: MKMapViewDelegate {
@IBOutlet var mapView: MKMapView!
var mapRegionTimer: NSTimer?
// MARK: MapView delegate
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
setMapRegionTimer()
}
func setMapRegionTimer() {
mapRegionTimer?.invalidate()
// Configure delay as bet fits your application
mapRegionTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "mapRegionTimerFired:", userInfo: nil, repeats: false)
}
func mapRegionTimerFired(sender: AnyObject) {
// Load markers for current region:
// mapView.centerCoordinate or mapView.region
}
}
別の可能な解決策は、次のように、マップビューを保持するビューコントローラーにtouchesMoved :(またはtouchesEnded:など)を実装することです。
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
for (UITouch * touch in touches) {
CGPoint loc = [touch locationInView:self.mapView];
if ([self.mapView pointInside:loc withEvent:event]) {
#do whatever you need to do
break;
}
}
}
これは、場合によっては、ジェスチャレコグナイザを使用するよりも簡単な場合があります。
InterfaceBuilderでマップにジェスチャレコグナイザーを追加することもできます。それをviewControllerでのアクションのアウトレットにリンクします。私は「mapDrag」と呼びました...
次に、viewControllerの.mで次のようなことを行います。
- (IBAction)mapDrag:(UIPanGestureRecognizer *)sender {
if(sender.state == UIGestureRecognizerStateBegan){
NSLog(@"drag started");
}
}
あなたもそこにこれを持っていることを確認してください:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
もちろん、それを機能させるには、.hファイルでviewControllerをUIGestureRecognizerDelegateにする必要があります。
それ以外の場合、ジェスチャイベントを聞くのはマップのレスポンダーだけです。
UIGestureRecognizerStateBegan
マップビューでジェスチャがいつ終了したかを認識するには:
これは、ユーザーがマップのズーム/回転/ドラッグを完了した後にのみデータベースクエリを実行する場合に非常に便利です。
私の場合、regionDidChangeAnimatedメソッドはジェスチャが実行された後にのみ呼び出され、ドラッグ/ズーム/回転中に何度も呼び出されることはありませんでしたが、ジェスチャが原因であるかどうかを知ることは役に立ちます。
これらのソリューションの多くはハッキーであり、Swiftが意図したものではないため、よりクリーンなソリューションを選択しました。
MKMapViewをサブクラス化し、touchesMovedをオーバーライドするだけです。このスニペットには含まれていませんが、移動に関して必要な情報を渡すためのデリゲートまたは通知を作成することをお勧めします。
import MapKit
class MapView: MKMapView {
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
print("Something moved")
}
}
このサブクラスを指すようにストーリーボードファイルのクラスを更新する必要があります。また、他の方法で作成したマップを変更する必要があります。
コメントに記載されているように、Appleはサブクラス化の使用を推奨していませんMKMapView
。これは開発者の裁量に委ねられますが、この特定の使用法はマップの動作を変更せず、3年以上問題なく機能してきました。ただし、過去のパフォーマンスは将来の互換性を示すものではないため、エンプターに注意してください。
Janoの答えは私にとってはうまくいったので、Objective Cに特に熟練していないので、Swift 4 / XCode 9の更新バージョンを残すと思いました。また、そうでないものもいくつかあると確信しています。
ステップ1:このコードをviewDidLoadに追加します:
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didDragMap(_:)))
panGesture.delegate = self
ステップ2:クラスがUIGestureRecognizerDelegateに準拠していることを確認します。
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, UIGestureRecognizerDelegate {
ステップ3:次の関数を追加して、panGestureが他のジェスチャと同時に機能することを確認します。
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
ステップ4:そして、Janoが正しく指摘しているように、メソッドが「ドラッグごとに50回」と呼ばれないようにします。
@objc func didDragMap(_ gestureRecognizer: UIPanGestureRecognizer) {
if (gestureRecognizer.state == UIGestureRecognizerState.ended) {
redoSearchButton.isHidden = false
resetLocationButton.isHidden = false
}
}
*最後のステップで@objcが追加されていることに注意してください。XCodeは、関数をコンパイルするために、このプレフィックスを関数に強制します。
私はこれが古い投稿であることを知っていますが、ここではJanoの答えのSwift4 / 5コードがパンとピンチのジェスチャーをします。
class MapViewController: UIViewController, MapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.didDragMap(_:)))
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(self.didPinchMap(_:)))
panGesture.delegate = self
pinchGesture.delegate = self
mapView.addGestureRecognizer(panGesture)
mapView.addGestureRecognizer(pinchGesture)
}
}
extension MapViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
@objc func didDragMap(_ sender: UIGestureRecognizer) {
if sender.state == .ended {
//code here
}
}
@objc func didPinchMap(_ sender: UIGestureRecognizer) {
if sender.state == .ended {
//code here
}
}
}
楽しい!
マップの中央に注釈を付けようとしていましたが、用途が何であれ、常にマップの中央にあります。私は上記のアプローチのいくつかを試しましたが、どれも十分ではありませんでした。私は最終的に、アンナの答えを借りて、エネコの答えと組み合わせるという、これを解決する非常に簡単な方法を見つけました。基本的に、regionWillChangeAnimatedをドラッグの開始として扱い、regionDidChangeAnimatedをドラッグの終了として扱い、タイマーを使用してピンをリアルタイムで更新します。
var mapRegionTimer: Timer?
public func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
mapRegionTimer?.invalidate()
mapRegionTimer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { (t) in
self.myAnnotation.coordinate = CLLocationCoordinate2DMake(mapView.centerCoordinate.latitude, mapView.centerCoordinate.longitude);
self.myAnnotation.title = "Current location"
self.mapView.addAnnotation(self.myAnnotation)
})
}
public func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
mapRegionTimer?.invalidate()
}
ここにコードを入力してくださいこれを最も簡単な方法で実装できました。これは、マップとのすべての対話(1/2 / N指でのタッピング/ダブル/ Nタッピング、1/2 / N指でのパン、ピンチ、回転)を処理します。
gesture recognizer
してマップビューのコンテナに追加しますgesture recognizer's
delegate
実装するオブジェクトに設定するUIGestureRecognizerDelegate
gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch)
メソッドを実装する
private func setupGestureRecognizers()
{
let gestureRecognizer = UITapGestureRecognizer(target: nil, action: nil)
gestureRecognizer.delegate = self
self.addGestureRecognizer(gestureRecognizer)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
self.delegate?.mapCollectionViewBackgroundTouched(self)
return false
}
まず、現在のViewControllerがマップのデリゲートであることを確認します。したがって、マップビューデリゲートをselfに設定し、ViewControllerに追加MKMapViewDelegate
します。以下の例。
class Location_Popup_ViewController: UIViewController, MKMapViewDelegate {
// Your view controller stuff
}
そしてこれをマップビューに追加します
var myMapView: MKMapView = MKMapView()
myMapView.delegate = self
次に、マップが移動したときに起動されるこの関数を追加します。アニメーションを除外し、操作された場合にのみ起動します。
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
if !animated {
// User must have dragged this, filters out all animations
// PUT YOUR CODE HERE
}
}
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
仕事をしました。