I currently have a GMSMapView with a UIView subview, but I can't get the subview to recognize tap gestures. I've tried many solutions like setting isUserInteractionEnabled equal to true and overriding the delegate but none have worked so far.
import UIKit
import GoogleMaps
class MapViewController: UIViewController, UIGestureRecognizerDelegate {
var testView: UIView!
var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
let camera = GMSCameraPosition.camera(withLatitude: 0, longitude: 0, zoom: 15.0)
mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.isUserInteractionEnabled = true
self.view = mapView
let screenSize: CGRect = UIScreen.main.bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
testView = UIView()
testView.backgroundColor = UIColor(white: 0, alpha: 0.5)
testView.frame.origin = CGPoint(x: 0, y: 0);
testView.frame.size = CGSize(width: screenWidth, height: screenHeight)
testView.isUserInteractionEnabled = true
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.doSomething(_:)))
gesture.numberOfTapsRequired = 1
gesture.numberOfTouchesRequired = 1
gesture.delegate = self
self.view.addSubview(testView)
testView.addGestureRecognizer(gesture)
}
#objc func doSomething(_ sender: UIGestureRecognizer) {
print("doSomething")
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if (touch.view == gestureRecognizer.view) {
print("returned true")
return true
}
return false
}
}
The interesting thing is that when I do tap on testView, it does print out "returned true" from my shouldReceiveTouch function. So I'm not quite sure how the delegate function returns true, yet the selector function isn't firing. I also tried this swipe gestures with another UIView and that did not work either. Any help is appreciated, thank you in advance!
Put the following code:
mapView.settings.consumesGesturesInView = false
From the Google Maps iOS SDK Reference:
Controls whether gestures by users are completely consumed by the
GMSMapView when gestures are enabled (default YES). This prevents
these gestures from being received by parent views.
When the GMSMapView is contained by a UIScrollView (or other
scrollable area), this means that gestures on the map will not be
additional consumed as scroll gestures. However, disabling this (set
to NO) may be useful to support complex view hierarchies or
requirements.
Related
I have two viewControllers:
ViewController1
A complex stack of sub viewcontrollers with somewhere in the middle an imageView
ViewController2
A scrollView with an imageView embedded in it
What I'm trying to achieve is a transition between the two viewControllers which gets triggered by pinching the imageView from viewController 1 causing it to zoom in and switch over to viewController 2. When the transition has ended, the imageView should be zoomed in as far as it's been zoomed during the pinch gesture triggered transition.
At the same time I want to support panning the image while performing the zoom transition so that just like with the zoom, the image in the end state will be transformed to the place it's been panned to.
So far I've tried the Hero transitions pod and a custom viewController transitions I wrote myself. The problem with the hero transitions is that the image doesn't properly get snapped to the end state in the second viewController. The problem I had with the custom viewController transition is that I couldn't get both zooming and panning to work at the same time.
Does anyone have an idea of how to implement this in Swift? Help is much appreciated.
The question can be divided in to two:
How to implement pinch zoom and dragging using pan gesture on an imageView
How to present a view controller with one of its subviews (imageView in vc2) positioned same as a subview (imageView in vc1) in the presenting view controller
Pinch gesture zoom: Pinch zooming is easier to implement using UIScrollView as it supports it out of the box with out a need to add the gesture recogniser. Create a scrollView and add the view you'd like to zoom with pinch as its subview (scrollView.addSubview(imageView)). Don't forget to add the scrollView itself as well (view.addSubview(scrollView)).
Configure the scrollView's min and max zoom scales: scrollView.minimumZoomScale, scrollView.maximumZoomScale. Set a delegate for scrollView.delegate and implement UIScrollViewDelegate:
func viewForZooming(in scrollView: UIScrollView) -> UIView?
Which should return your imageView in this case and,
Also conform to UIGestureRecognizerDelegate and implement:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool
Which should return true. This is the key that allows us have pan gesture recogniser work with the internal pinch gesture recogniser.
Pan gesture dragging: Simply create a pan gesture recogniser with a target and add it to your scroll view scrollView.addGestureRecognizer(pan).
Handling gestures: Pinch zoom is working nicely by this stage except you'd like to present the second view controller when pinching ends. Implement one more UIScrollViewDelegate method to be notified when zooming ends:
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat)
And call your method that presents the detail view controller presentDetail(), we'll implement it in a bit.
Next step is to handle the pan gesture, I'll let the code explain itself:
// NOTE: Do NOT set from anywhere else than pan handler.
private var initialTouchPositionY: CGFloat = 0
private var initialTouchPositionX: CGFloat = 0
#objc func panned(_ pan: UIPanGestureRecognizer) {
let y = pan.location(in: scrollView).y
let x = pan.location(in: scrollView).x
switch pan.state {
case .began:
initialTouchPositionY = pan.location(in: imageView).y
initialTouchPositionX = pan.location(in: imageView).x
case .changed:
let offsetY = y - initialTouchPositionY
let offsetX = x - initialTouchPositionX
imageView.frame.origin = CGPoint(x: offsetX, y: offsetY)
case .ended:
presentDetail()
default: break
}
}
The implementation moves imageView around following the pan location and calls presentDetail() when gesture ends.
Before we implement presentDetail(), head to the detail view controller and add properties to hold imageViewFrame and the image itself. Now in vc1, we implement presentDetail() as such:
private func presentDetail() {
let frame = view.convert(imageView.frame, from: scrollView)
let detail = DetailViewController()
detail.imageViewFrame = frame
detail.image = imageView.image
// Note that we do not need the animation.
present(detail, animated: false, completion: nil)
}
In your DetailViewController, make sure to set the imageViewFrame and the image in e.g. viewDidLoad and you'll be set.
Complete working example:
class ViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate {
let imageView: UIImageView = UIImageView()
let scrollView: UIScrollView = UIScrollView()
lazy var pan: UIPanGestureRecognizer = {
return UIPanGestureRecognizer(target: self, action: #selector(panned(_:)))
}()
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = // set your image
scrollView.delegate = self
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 10.0
scrollView.addSubview(imageView)
view.addSubview(scrollView)
scrollView.frame = view.frame
let w = view.bounds.width - 30 // padding of 15 on each side
imageView.frame = CGRect(x: 0, y: 0, width: w, height: w)
imageView.center = scrollView.center
scrollView.addGestureRecognizer(pan)
}
// NOTE: Do NOT set from anywhere else than pan handler.
private var initialTouchPositionY: CGFloat = 0
private var initialTouchPositionX: CGFloat = 0
#objc func panned(_ pan: UIPanGestureRecognizer) {
let y = pan.location(in: scrollView).y
let x = pan.location(in: scrollView).x
switch pan.state {
case .began:
initialTouchPositionY = pan.location(in: imageView).y
initialTouchPositionX = pan.location(in: imageView).x
case .changed:
let offsetY = y - initialTouchPositionY
let offsetX = x - initialTouchPositionX
imageView.frame.origin = CGPoint(x: offsetX, y: offsetY)
case .ended:
presentDetail()
default: break
}
}
// MARK: UIScrollViewDelegate
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
presentDetail()
}
// MARK: UIGestureRecognizerDelegate
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
// MARK: Private
private func presentDetail() {
let frame = view.convert(imageView.frame, from: scrollView)
let detail = DetailViewController()
detail.imageViewFrame = frame
detail.image = imageView.image
present(detail, animated: false, completion: nil)
}
}
class DetailViewController: UIViewController {
let imageView: UIImageView = UIImageView()
var imageViewFrame: CGRect!
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
imageView.frame = imageViewFrame
imageView.image = image
view.addSubview(imageView)
view.addSubview(backButton)
}
lazy var backButton: UIButton = {
let button: UIButton = UIButton(frame: CGRect(x: 10, y: 30, width: 60, height: 30))
button.addTarget(self, action: #selector(back(_:)), for: .touchUpInside)
button.setTitle("back", for: .normal)
return button
}()
#objc func back(_ sender: UIButton) {
dismiss(animated: false, completion: nil)
}
}
seems like UIView.animate(withDuration: animations: completion:) should help you; for example, in animations block you can set new image frame, and in completion: - present second view controller (without animation);
I am new to Xcode and mobile app. I am doing an app to find the current location. I tested it on the simulator and got this message in the console.
"Could not inset legal attribution from corner 4". What does it mean and how can I fix it?
import UIKit
import Alamofire
import AlamofireImage
import MapKit
import CoreLocation
class MapVC: UIViewController
#IBOutlet weak var mapView: MKMapView!
var locationManager = CLLocationManager()
let authorizationStatus = CLLocationManager.authorizationStatus()
let regionRadius: Double = 1000
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
configureLocationServices()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func centerMapPressed(_ sender: Any) {
if authorizationStatus == .authorizedAlways || authorizationStatus == .authorizedWhenInUse{
centerMapOnUserLocation()
}
}
MKMapViewDelegate:
func centerMapOnUserLocation(){
guard let coordinate = locationManager.location?.coordinate else{return}
let coordinateRegion = MKCoordinateRegionMakeWithDistance(coordinate, regionRadius*2.0, regionRadius * 2.0 )
mapView.setRegion(coordinateRegion, animated: true)
}
CLLocationManagerDelegate:
func configureLocationServices(){
if authorizationStatus == .notDetermined{
locationManager.requestAlwaysAuthorization()
}else{
return}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
centerMapOnUserLocation()
}
this issue occurs when the required information in the right form is not found. if your device i trying to get some location or some number a variable, and that a variable is required for afun.. the value is not set into a, but afun is called that this error occurs..
call your cell's coordinates in viewdidload without any other function.
Here is the simplest way to get your cell's position.
1. you should have updated privacies of the devices under infor.plist
Add following lines into your plist before closing tag
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Privacy - Location Always and When In Use Usage Description</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Privacy - Location Always Usage Description</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Privacy - Location When In Use Usage Description</string>
after that.. this is the simplest working code .. i'm happily using it with no issue..
import CoreLocation
import MapKit
class AddressVc: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var map: MKMapView!
var locationManager = CLLocationManager()
here is code to move map in center for the required location
let latDelta:Double = 0.5
let lngDelta:Double = 0.5
let latitude:Double = 37.57554038
let longitude:Double = -122.40068475
let locationcoordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let zoomSpan = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lngDelta)
let region = MKCoordinateRegion(center: locationcoordinates, span: zoomSpan)
self.map.setRegion(region, animated: true)
in the above code.. if you put device's lat and lng postions in latitude and lognitude that will move the map to your's devices's location.
now how to get device's location.
here is the code you can put into your viewDidLoad() function.
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
// device latitude = (locationManager.location?.coordinate.latitude)!
// device longitude = (locationManager.location?.coordinate.longitude)!
}
this is detect the device's latitude and longitude values. you can put them in above mentioned code..
Here is the full code.
import CoreLocation
import MapKit
class AddressVc: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var map: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
devicelatitude = (locationManager.location?.coordinate.latitude)!
devicelongitude = (locationManager.location?.coordinate.longitude)!
let latDelta:Double = 0.5
let lngDelta:Double = 0.5
let latitude:Double = devicelatitude
let longitude:Double = devicelongitude
let locationcoordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let zoomSpan = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lngDelta)
let region = MKCoordinateRegion(center: locationcoordinates, span: zoomSpan)
self.map.setRegion(region, animated: true)
}
I hope this will help you.. if not.. there would be many other who are facing this issue.
==========================
With the simulator open, go to the DEBUG menu on the mac top bar, the last option is location, mine was showing this error when set to NONE. Set it Custom Location, first with you prompt you to a custom location on a new window, fill that, close the simulator, and relaunch the app. It should get the custom location from your code now. (andrecasarini credit)
This error occurs when the "Legal" link on the bottom left of the MapView is not within bounds or is obscured. There are similar errors for the map's scale and for the compass that appears when rotating.
Consider using size constraints or a safeAreaInset.
I believe the legal inset bug is in Apple's mapkit initialization code and unrelated to what we do when we use the code. Here's my reasoning.
If you use storyboards and the UIMapKit drag and drop module, the legal inset error pops up somewhere between the call to the initial call to application in the app delegate and the first viewDidLoad call, i.e., it's an OS error.
I got curious if I could work around it and wrote a version that doesn't use the storyboard editor. I was thinking that perhaps the editor was inserting some broken code into the app. My test app has two views, the start view and a second view with the map on it. The code is shown below. Don't be harsh, it's my first attempt at understanding what's going on with controllers views and subviews using programmatic views and subviews. It isn't pretty but it works to isolate the legal inset bug.
I started by building a new xcode project and deleting the storyboard. I then replaced the application function stub in appdelegate with the following code:
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
self.window = UIWindow(frame: UIScreen.main.bounds)
let viewController = ViewController()
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
let mapView = mapViewController()
mapView.otherView = viewController
viewController.mapView = mapView
return true
}
Even though the mapView is created here, the legal inset error doesn't show up. What does show up are a couple of other messages about scale and compass having some sort of issue. Since I was focusing on the legal inset bug, I ignored them and moved on.
I then replaced the default view controller with code that created two subviews, one a subview that serves as a button to switch to the second view controller and the second subview that serves as "you're on the first view" marker view. That required figuring out how to handle a tap. The color changing code in the tap handler was initial "hello world. I see a tap" code and serves no other purpose.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate {
let DynamicView = UIView()
let switchView = UIView(frame: CGRect(x: UIScreen.main.bounds.minX+10, y: UIScreen.main.bounds.minY+20, width: 40, height: 40))
public var mapView:mapViewController? = nil
public var otherView:UIViewController? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = .white
DynamicView.backgroundColor = .yellow
DynamicView.layer.cornerRadius = 25
DynamicView.layer.borderWidth = 2
switchView.backgroundColor = .orange
switchView.layer.borderWidth = 2
var gR = UITapGestureRecognizer(target: self, action:#selector(self.handlebigTap(_:)))
DynamicView.addGestureRecognizer(gR)
gR = UITapGestureRecognizer(target: self, action:#selector(self.handlesmallTap(_:)))
switchView.addGestureRecognizer(gR)
self.view.addSubview(switchView)
self.view.addSubview(DynamicView)
DynamicView.translatesAutoresizingMaskIntoConstraints = false
let margins = view.layoutMarginsGuide
DynamicView.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: 10).isActive = true
DynamicView.trailingAnchor.constraint(equalTo: margins.trailingAnchor, constant: 10).isActive = true
DynamicView.topAnchor.constraint(equalTo: margins.topAnchor, constant: 40).isActive = true
DynamicView.bottomAnchor.constraint(equalTo: margins.topAnchor, constant: 110).isActive = true
}
#objc func handlebigTap(_ sender: UITapGestureRecognizer) {
if ( self.DynamicView.backgroundColor == .green){
self.DynamicView.backgroundColor = .blue
} else {
self.DynamicView.backgroundColor = .green
}
}
#objc func handlesmallTap(_ sender: UITapGestureRecognizer) {
if ( self.switchView.backgroundColor == .orange){
self.switchView.backgroundColor = .blue
present(mapView!, animated: true, completion: nil)
} else {
self.switchView.backgroundColor = .orange
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
At this point, I checked to see if the first call to viewDidLoad triggered the legal inset error and saw that it did not. That meant the error was being triggered somewhere in the mapKit initialization code which was yet to be built. I simply copied and pasted the first viewController into a new file and called that mapViewController. I commented out the DynamicView code left over from the first controller and added the mapKit initialization code as shown here:
import UIKit
import MapKit
import CoreLocation
class mapViewController: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate {
let DynamicView = UIView(frame: CGRect(x: UIScreen.main.bounds.maxX-110, y: UIScreen.main.bounds.maxY-110, width: 100, height: 100))
let switchView = UIView(frame: CGRect(x: UIScreen.main.bounds.minX+10, y: UIScreen.main.bounds.minY+20, width: 40, height: 40))
var mapView = MKMapView()
public var otherView:UIViewController? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = .white
// DynamicView.backgroundColor = .yellow
// DynamicView.layer.cornerRadius = 25
// DynamicView.layer.borderWidth = 2
switchView.backgroundColor = .orange
switchView.layer.borderWidth = 2
var gR = UITapGestureRecognizer(target: self, action:#selector(self.handlebigTap(_:)))
DynamicView.addGestureRecognizer(gR)
gR = UITapGestureRecognizer(target: self, action:#selector(self.handlesmallTap(_:)))
switchView.addGestureRecognizer(gR)
self.view.addSubview(switchView)
// self.view.addSubview(DynamicView)
// mapView.frame = CGRect(x: 10, y: 60, width: view.frame.size.width-20, height: 300)
mapView.mapType = MKMapType.standard
mapView.isZoomEnabled = true
mapView.isScrollEnabled = true
view.addSubview(mapView)
mapView.translatesAutoresizingMaskIntoConstraints = false
let margins = view.layoutMarginsGuide
mapView.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: 10).isActive = true
mapView.trailingAnchor.constraint(equalTo: margins.trailingAnchor, constant: 0).isActive = true
mapView.topAnchor.constraint(equalTo: margins.topAnchor, constant: 45).isActive = true
mapView.bottomAnchor.constraint(equalTo: margins.bottomAnchor, constant: -20).isActive = true
}
#objc func handlebigTap(_ sender: UITapGestureRecognizer) {
if ( self.DynamicView.backgroundColor == .green){
self.DynamicView.backgroundColor = .blue
} else {
self.DynamicView.backgroundColor = .green
}
}
#objc func handlesmallTap(_ sender: UITapGestureRecognizer) {
if ( self.switchView.backgroundColor == .orange){
self.switchView.backgroundColor = .blue
dismiss(animated: true, completion: nil)
} else {
self.switchView.backgroundColor = .orange
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
}
}
I ran the above code and stepped through looking for the legal inset message to show up. This is the offending line:
mapView.frame = CGRect(x: 10, y: 60, width: view.frame.size.width-20, height: 300)
As soon as the view is framed, the error message comes out. Doesn't matter what dimensions you give the view, the message appears. OK.... Maybe the message expects a constraint based framing instead of hard coded per the offending line.
I commented out the line and added constraints to the view and still the error popped up.
At this point I gave up. I couldn't figure out how to configure a map view so the error message doesn't show up. Hopefully Apple will pipe up and say something at this point or someone else will pick up the baton to see if there's a way to configure mapkit to stop spewing error messages.
As for myself at least I learned how to dynamically add views, subviews, gesture recognizers and constraints so my time was profitably spent chasing the bug. Thanks to all who posted sample dynamic view code, gesture recognition code, subview code and constraint code. You may recognize a bit of your code here.
You have not typed in an opening curly bracket for your MapVC Class:
your code:
class MapVC: UIViewController
to fix:
class MapVC: UIViewController {
As you can see in the image below I have a video which covers almost the entire screen. I want the video to play/stop whenever one taps on it. The code works almost perfectly fine, the thing is the class in which I call the defInteractions function also contains the booked and comments subview which can also be found below. Consequently, the video also plays/stops when one taps these areas which I don't want.
The UITapGestureRecognizer triggering the function to play/pause the video:
//set interactions
func defInteractions (){
//singletap
let singleTap = UITapGestureRecognizer(target: self, action: #selector(singleTapDetected(_:)))
singleTap.numberOfTapsRequired = 1
//singleTap.cancelsTouchesInView = false
//controlsContainerView
controlsContainerView.addGestureRecognizer(singleTap)
}
//define type
var player: AVPlayer?
//set playing to false
var isPlaying: Bool = false
func singleTapDetected(_ sender: UITapGestureRecognizer) {
//play or pause
if(!isPlaying){
//play
player?.play()
isPlaying = true
}
else{
//pause
player?.pause()
isPlaying = false
}
}
Each subview looks basically like this:
//create controls container view
let comments: UIView = {
//set properties of controls container view
let commentrect = CGRect(x: viewWidth / 2, y: viewHeight - 110, width: viewWidth / 2, height: 50)
let entireCommentView = UILabel(frame: commentrect)
entireCommentView.translatesAutoresizingMaskIntoConstraints = true
entireCommentView.backgroundColor = .white
entireCommentView.font = UIFont(name: "HelveticaNeue", size: 20)
entireCommentView.text = "3 comments"
entireCommentView.textColor = .black
entireCommentView.textAlignment = .center
return entireCommentView
}()
In the override they are added as subviews. I tried setting isUserInteractionEnabled to false in the individual subviews (e.g. comments: entireCommentView.isUserInteractionEnabled = false) which didn't work and don't know how to achieve my goal. Can someone help me? Can I exclude these subviews from my target in the UITapGestureRecognizer recognizer.
[
EDIT (Result of first answer):
Modify the singleTapDetected to return if the tap is in any of the top or bottom UIViews.
func singleTapDetected(_ sender: UITapGestureRecognizer) {
let view = sender.view
let loc = sender.location(in: controlsContainerView)
if let subview = view?.hitTest(loc, with: nil) {
if subview == entireCommentView || subview == bookedView {
return
}
}
//play or pause
player?.rate != 0 && player?.error == nil ? player?.pause() : player?.play
}
Alternatively, you can have your UIViewController adopt the UIGestureRecognizerDelegate protocol and call the below method for checking the view. Inside this method, check your view against touch.view and return the appropriate bool (Yes/No). Something like this:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return !touch.view?.isDescendant(of: controlsContainerView)
}
You should define the likes and booked variables as property of the UIView subclass itself, to make them visible to your singleTapDetected(_:) method
in this I had implemented the swipe gestures on image view and it is embedded in scroll view but gestures are not working here is my code any solution for this ?
collectionView.delegate = self
collectionView.dataSource = self
imageView.isUserInteractionEnabled = true
let swipeLeft = UISwipeGestureRecognizer(target: self, action:#selector(swiped(gesture:)))
swipeLeft.direction = .left
self.imageView.addGestureRecognizer(swipeLeft)
swipeLeft.cancelsTouchesInView = false
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(swiped(gesture:)))
swipeRight.direction = .right
self.imageView.addGestureRecognizer(swipeRight)
swipeRight.cancelsTouchesInView = false
imageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(imageTapped(_:)))
self.imageView.addGestureRecognizer(tap)
If you want swipe and scrolling both at work concurrently you have to implement gesture recognizer delegate.
// here are those protocol methods with Swift syntax
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
return true
}
May be you could try this.Don't forget to delegate as self. :)
For your case, you want to have UI like Gallery View. Just Left and right swipe to change photo and tap/DoubleTap to zoom.
Don't need Swipe Gestures.
Just use Collection view with paging enabled. Each cell will display a single image. when Paging enabled, only a single cell will be shown at a time.
So just enable Paging enabled in collection view and try running.
If you want to zoom, when tapping a cell then, add TapGesture to CollectionView's parent UIScrollView and do write actions correspondingly.
For this case, I've used custom collection view cell.
I've just added my custom cell code for your reference as given below.
class ImageViewerCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func awakeFromNib() {
super.awakeFromNib()
self.scrollView.minimumZoomScale = 1.0
self.scrollView.maximumZoomScale = 6.0
self.scrollView.delegate = self
self.scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
self.scrollView.zoom(to: CGRect(origin: CGPoint.zero, size: scrollView.frame.size), animated: true)
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(doubleTapAction(_:)))
doubleTap.numberOfTapsRequired = 2
self.scrollView.addGestureRecognizer(doubleTap)
}
func setURL(imageURL : URL, needLoader : Bool) {
DispatchQueue.main.async {
self.scrollView.setZoomScale(self.scrollView.minimumZoomScale, animated: true)
self.scrollView.zoom(to: CGRect(origin: CGPoint.zero, size: self.scrollView.frame.size), animated: true)
if needLoader {
self.activityIndicator.isHidden = false
self.activityIndicator.startAnimating()
self.imageView.pin_setImage(from: imageURL, completion: { (completed) in
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
})
}
else {
self.activityIndicator.isHidden = true
self.imageView.pin_setImage(from: imageURL, placeholderImage: placeHolderImage)
}
self.imageView.pin_updateWithProgress = true
}
}
#IBAction func doubleTapAction(_ sender: Any) {
if scrollView.zoomScale == scrollView.minimumZoomScale {
let touchPoint = (sender as! UITapGestureRecognizer).location(in: scrollView)
let scale = min(scrollView.zoomScale * 3, scrollView.maximumZoomScale)
let scrollSize = scrollView.frame.size
let size = CGSize(width: scrollSize.width / scale,
height: scrollSize.height / scale)
let origin = CGPoint(x: touchPoint.x - size.width / 2,
y: touchPoint.y - size.height / 2)
scrollView.zoom(to:CGRect(origin: origin, size: size), animated: true)
}
else {
scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
scrollView.zoom(to: CGRect(origin: CGPoint.zero, size: scrollView.frame.size), animated: true)
}
}
}
extension ImageViewerCollectionViewCell : UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return self.imageView
}
}
As given above, I've used a imageview inside UIScrollView. So when you double tapped that scroll view, scrollview will zoom in & out.
Hope you understand and hope it helps for sure.
I am creating an app using swift. In one of my ViewController, I have a GMSMapView that I create programatically. I want user to have the capability triggering an action when clicking on the map.
What I have done :
import UIKit
class MapViewController: UIViewController, GMSMapViewDelegate {
let mapView = GMSMapView()
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.settings.scrollGestures = false
mapView.frame = CGRectMake(0, 65, 375, 555)
view.addSubview(mapView)
var tap = UITapGestureRecognizer(target: self, action: "tap:")
mapView.addGestureRecognizer(tap)
}
func tap(recogniser:UITapGestureRecognizer)->Void{
println("it works")
}
}
I have tried to override touchesBegan, didnt work. I have tried to insert mapView.userInteractionEnabled = true, didnt work...
Any idea?
I managed to do it with
func mapView(mapView: GMSMapView!, didTapAtCoordinate coordinate: CLLocationCoordinate2D) {
println("It works")
}
But if someone could explain to me why the other solution didn't work, it would be great!
You can use default MapVIew LongPress event
/**
* Called after a long-press gesture at a particular coordinate.
*
* #param mapView The map view that was pressed.
* #param coordinate The location that was pressed.
*/
- (void)mapView:(GMSMapView *)mapView
didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate;
The map view already has its own gesture recognizers for panning, zooming etc.
So you probably need to tell the system that it should take care on multiple gesture recognizers.
As part of the UIGestureRecognizerDelegate protocol:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}