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:そして、MKMapViewの既存のジェスチャレコグナイザーで動作するUIPanGestureRecognizerの次のコードを追加します。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
ステップ4:ドラッグごとにメソッドを1回50回呼び出す場合は、セレクターで「ドラッグ終了」状態を検出します。
- (void)didDragMap:(UIGestureRecognizer*)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
NSLog(@"drag ended");
}
}
これは、ユーザーが開始したパンとズームの変更を検出する唯一の方法です。
- (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
}
}
(ちょうど)Swift @ mobiの優れたソリューションのバージョン :
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
}
}
Janoの答え 上記に対するSwift 3ソリューション:
ViewControllerにプロトコルUIGestureRecognizerDelegateを追加します
class MyViewController: UIViewController, UIGestureRecognizerDelegate
viewDidLoad
にUIPanGestureRecognizerを作成し、delegate
をselfに設定します
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
}
}
別の可能な解決策は、マップビューを保持するView Controllerで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;
}
}
}
場合によっては、これはジェスチャレコグナイザーを使用するよりも簡単です。
Interface Builderで、マップにジェスチャーレコグナイザーを追加することもできます。それをあなたの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にする必要があります。
そうでない場合、ジェスチャーイベントを聞くのはマップのレスポンダーだけです。
これらの解決策の多くは、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")
}
}
このサブクラスを指すようにストーリーボードファイルのクラスを更新し、他の方法で作成したマップを変更する必要があります。
Falseの場合、アニメーション化されたプロパティをチェックしてから、ユーザーがマップをドラッグできます。
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
if animated == false {
//user dragged map
}
}
マップビューでジェスチャーが終了したことを認識するには:
http://b2cloud.com.au/tutorial/mkmapview-determining-whether-region-change-is-from-user-interaction/
これは、ユーザーが地図のズーム/回転/ドラッグを完了した後にのみデータベースクエリを実行する場合に非常に便利です。
私にとって、regionDidChangeAnimatedメソッドはジェスチャーが行われた後にのみ呼び出され、ドラッグ/ズーム/回転中に何度も呼び出されませんでしたが、ジェスチャーによるものかどうかを知ることは有用です。
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は、コンパイルするためにこのプレフィックスを関数に強制します。
私はこれが古い投稿であることを知っていますが、ここではSwift 4/5 Janoの答え 聖霊降臨祭のパンとピンチのジェスチャーのコードです。
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
}
}
}
楽しい!
ここにコードを入力しますこれを最も簡単な方法で実装し、マップとのすべての相互作用を処理しました(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 }
_
最初、現在のView Controllerがマップのデリゲートであることを確認します。したがって、Map Viewデリゲートをselfに設定し、MKMapViewDelegate
をView Controllerに追加します。以下の例。
class Location_Popup_ViewController: UIViewController, MKMapViewDelegate {
// Your view controller stuff
}
これをマップビューに追加します
var myMapView: MKMapView = MKMapView()
myMapView.delegate = self
Second、マップが移動したときに起動されるこの関数を追加します。アニメーションを除外し、対話した場合にのみ起動します。
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
if !animated {
// User must have dragged this, filters out all animations
// PUT YOUR 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()
}