Unrecognized selector sent to instance when using UIPanGesture - swift2

Hi I am trying to add UIPanGestureRecognizer to UIImageView (in my case, it's an emoji). All other UIGestureRecognizers such as long press, rotation, and pinch work well. However, it gives me an error: unrecognized selector sent to instance when I add UIPanGestureRecognizer. I've spent a day trying to figure out the reason but failed to fix it. Please help! Thanks in advance.
This is a function where I added UIGestureRecognizer to sticker
func emojiInsert(imageName: String) {
deleteButtonHides()
let stickerView: UIImageView = UIImageView(frame: CGRectMake(backgroundImage.frame.width/2 - 50, backgroundImage.frame.height/2 - 50, stickerSize, stickerSize))
stickerView.image = UIImage(named: imageName)
stickerView.userInteractionEnabled = true
stickerView.accessibilityIdentifier = "sticker"
let deleteStickerButton: UIImageView = UIImageView(frame: CGRectMake(stickerView.frame.width - 5 - stickerView.frame.width/3, 5, stickerView.frame.width/3, stickerView.frame.height/3))
deleteStickerButton.image = UIImage(named: "button_back")
deleteStickerButton.accessibilityIdentifier = "delete"
deleteStickerButton.userInteractionEnabled = true
deleteStickerButton.alpha = 0
deleteStickerButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "deleteButtonTouches:"))
stickerView.addSubview(deleteStickerButton)
stickerView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: "handlePinch:"))
stickerView.addGestureRecognizer(UIRotationGestureRecognizer(target: self, action: "handleRotate:"))
stickerView.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: "handleLongPress:"))
stickerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan"))
print("emojiInsert : \(imageName)")
backgroundImage.addSubview(stickerView)
}
Below are call back functions I added in the end of the view.swift. I used touchesbegan and touchesMoved to drag an emoji but emoji moved in weird way after rotation. So now I am trying to use UIPanGesture to drag an emoji.
#IBAction func handlePinch(recognizer : UIPinchGestureRecognizer) {
if(deleteMode) {
return
}
print("handlePinch \(recognizer.scale)")
if let view = recognizer.view {
view.transform = CGAffineTransformScale(view.transform,
recognizer.scale, recognizer.scale)
recognizer.scale = 1
}
}
#IBAction func handleRotate(recognizer : UIRotationGestureRecognizer) {
if(deleteMode) {
return
}
if let view = recognizer.view {
view.transform = CGAffineTransformRotate(view.transform, recognizer.rotation)
recognizer.rotation = 0
}
}
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
if(deleteMode) {
return
}
let translation = recognizer.translationInView(self.view)
if let view = recognizer.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
recognizer.setTranslation(CGPointZero, inView: self.view)
}
#IBAction func handleLongPress(recognizer: UILongPressGestureRecognizer) {
if(recognizer.state == UIGestureRecognizerState.Began) {
if(!deleteMode) {
print("LongPress - Delete Shows")
for (_, stickers) in self.backgroundImage.subviews.enumerate() {
for (_, deleteButtons) in stickers.subviews.enumerate() {
if let delete:UIImageView = deleteButtons as? UIImageView{
if(delete.accessibilityIdentifier == "delete") {
delete.alpha = 1
}
}
}
}
deleteMode = true
} else {
deleteButtonHides()
}
}
}
Again, please help! Thanks in advance.

The problem is that you're missing a colon. In the following line:
stickerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan"))
The handlePan should be handlePan:. That's because the Objective-C signature for your method is:
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
The colon is part of the method name.

Related

Not able to Rotate my 3D objects

I had created one project using ARKit and SceneKit framework. In which I am working with file extension .dae, the files are locally available in my project as shown in below screenshot.
Here I had applied many gestures on this virtual object such as Tap Gesture(When I tap on camera screen, it places the virtual object there), same way Pinch Gesture and Pan Gesture. All of these gestures are working perfectly fine. Now I wanted to apply rotation gesture, for which I got stuck how to do that, also I am not getting any such available sources to achieve this.
Below is my working code so far,
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
private var movedObject: SCNNode?
private var hud :MBProgressHUD!
override func viewDidLoad() {
super.viewDidLoad()
self.sceneView.autoenablesDefaultLighting = true
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene()
sceneView.scene = scene
registerGestureRecognizers()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
private func registerGestureRecognizers() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped(recognizer:)))
tapGestureRecognizer.numberOfTapsRequired = 1
self.sceneView.addGestureRecognizer(tapGestureRecognizer)
let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(pinched(recognizer:)))
self.sceneView.addGestureRecognizer(pinchGestureRecognizer)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(moveObject(recognizer:)))
panGestureRecognizer.maximumNumberOfTouches = 1
panGestureRecognizer.minimumNumberOfTouches = 1
self.sceneView.addGestureRecognizer(panGestureRecognizer)
let rotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(rotateObject(recognizer:)))
self.sceneView.addGestureRecognizer(rotationGestureRecognizer)
}
#objc func pinched(recognizer :UIPinchGestureRecognizer) {
if recognizer.state == .changed {
guard let sceneView = recognizer.view as? ARSCNView else {
return
}
let touch = recognizer.location(in: sceneView)
let hitTestResults = self.sceneView.hitTest(touch, options: nil)
if let hitTest = hitTestResults.first {
let chairNode = hitTest.node
let pinchScaleX = Float(recognizer.scale) * chairNode.scale.x
let pinchScaleY = Float(recognizer.scale) * chairNode.scale.y
let pinchScaleZ = Float(recognizer.scale) * chairNode.scale.z
chairNode.scale = SCNVector3(pinchScaleX,pinchScaleY,pinchScaleZ)
recognizer.scale = 1
}
}
}
#objc func moveObject(recognizer: UIPanGestureRecognizer) {
print("Move object")
if recognizer.state == .began {
print("Pan state began")
let tapPoint: CGPoint? = recognizer.location(in: sceneView)
let result = sceneView.hitTest(tapPoint ?? CGPoint.zero, options: nil)
if result.count == 0 {
return
}
let hitResult: SCNHitTestResult? = result.first
if (hitResult?.node.name == "free_car_1") {
movedObject = hitResult?.node
} else if (hitResult?.node.parent?.name == "free_car_1") {
movedObject = hitResult?.node.parent
}
if (movedObject != nil) {
print("Holding an Object")
}
}
if recognizer.state == .changed {
print("Pan State Changed")
if (movedObject != nil) {
let tapPoint: CGPoint? = recognizer.location(in: sceneView)
let hitResults = sceneView.hitTest(tapPoint ?? CGPoint.zero, types: .featurePoint)
let result: ARHitTestResult? = hitResults.last
let matrix: SCNMatrix4 = SCNMatrix4((result?.worldTransform)!)
//SCNMatrix4FromMat4((result?.worldTransform)!)
let vector: SCNVector3 = SCNVector3Make(matrix.m41, matrix.m42, matrix.m43)
movedObject?.position = vector
print("Moving object position")
}
}
if recognizer.state == .ended {
print("Done moving object homeie")
movedObject = nil
}
}
#objc func tapped(recognizer :UITapGestureRecognizer) {
guard let sceneView = recognizer.view as? ARSCNView else {
return
}
let touch = recognizer.location(in: sceneView)
let hitTestResults = sceneView.hitTest(touch)
guard let hitTest = hitTestResults.first?.node else {
let hitTestResultsWithExistingPlane = sceneView.hitTest(touch, types: .existingPlane)
let chairScene = SCNScene(named: "ShelbyWD.dae")!
guard let chairNode = chairScene.rootNode.childNode(withName: "ShelbyWD", recursively: true) else {
return
}
if let hitTestAvailable = hitTestResultsWithExistingPlane.first {
chairNode.position = SCNVector3(hitTestAvailable.worldTransform.columns.3.x,hitTestAvailable.worldTransform.columns.3.y,hitTestAvailable.worldTransform.columns.3.z)
self.sceneView.scene.rootNode.addChildNode(chairNode)
return
}
return
}
hitTest.removeFromParentNode()
}
#objc func rotateObject(recognizer :UIRotationGestureRecognizer)
{
}
}
Can anyone help me out to apply rotation gesture on my object?
Thank you!
In order to rotate an SCNNode, the 1st thing you need to do, is create a variable to store the rotationAngle around the YAxis or any other that you wish to perform the rotation on e.g:
var currentAngleY: Float = 0.0
Then have some way to have detected to node you wish to rotate, which in my example I am calling currentNode e.g.
var currentNode: SCNNode!
In my example I will just rotate around the YAxis.
You can use a UIPanGestureRecognizer like so:
/// Rotates An Object On It's YAxis
///
/// - Parameter gesture: UIPanGestureRecognizer
#objc func rotateObject(_ gesture: UIPanGestureRecognizer) {
guard let nodeToRotate = currentNode else { return }
let translation = gesture.translation(in: gesture.view!)
var newAngleY = (Float)(translation.x)*(Float)(Double.pi)/180.0
newAngleY += currentAngleY
nodeToRotate.eulerAngles.y = newAngleY
if(gesture.state == .ended) { currentAngleY = newAngleY }
print(nodeToRotate.eulerAngles)
}
Or if you wish to use a UIRotationGesture you can do something like this:
/// Rotates An SCNNode Around It's YAxis
///
/// - Parameter gesture: UIRotationGestureRecognizer
#objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
//1. Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
if gesture.state == .changed{
isRotating = true
currentNode.eulerAngles.y = currentAngleY + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The Cube
if(gesture.state == .ended) {
currentAngleY = currentNode.eulerAngles.y
isRotating = false
}
}
Hope it helps...

Found nil while unwrapping an optional value - Swift 2

I am getting this error which I cannot explain:
#IBOutlet weak var licencePlateLabel: UILabel!
var editItem: CarValues? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let editedCar = self.editItem {
if let licencePlate = self.licencePlateLabel {
licencePlate.text? = editedCar.licencePlate!//this gives me nil
}
else {
print("value was nil")
}
print(editedCar.licencePlate!)//this is giving me the correct value
}
if I replace the
if let licencePlate = self.licencePlateLabel {
licencePlate.text! = editedCar.licencePlate!
}//this throws an error "found nil......"
even if I do this I m still getting the "found nil..."
func configureView() {
licencePlateLabel.text = "test"
[...]
}
BUT if I put the above on viewDidLoad then it works fine
override func viewDidLoad() {
licencePlateLabel.text = "test"
[...]
}
What is going on in this code?
EDIT
I am passing the value of the editItem from the detailView to the EditView like this:
#IBAction func editButtonTapped(sender: AnyObject) {
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let editScreen = storyBoard.instantiateViewControllerWithIdentifier("ID_EditViewController")
self.navigationController?.pushViewController(editScreen, animated: true)
let controller = EditViewController()
controller.editItem = detailItem
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
You don't unwrap properties to set them, only to read from them. So your code can simply be:
if let licencePlate = self.licencePlateLabel {
licencePlate.text = editedCar.licencePlate
}
Note that because licencePlate.text is an optional value anyway, there is also no need to unwrap editedCar.licencePlate. It's ok to use its value whether it is nil or contains a String.

How to show route between a MKPointAnnotation and user's current location in swift 2

I am trying to show the route between a MKPointAnnotation and user's current location, but i am fail with it.
My idea is: getting user's current location -> getting the MKPointAnnotation' Coordinate -> line up with MKPolylineRenderer
The problem is that i cannot find the problem. :( I have no idea where i should modify.
class MapInSearch: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
var destination: MKMapItem?
var coords: CLLocationCoordinate2D?
let locationManager = CLLocationManager()
var PlaceLat = ""
var PlaceLong = ""// get from previous view controller
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestAlwaysAuthorization()
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}// step 1
self.mapView.showsUserLocation = true
self.mapView.delegate = self
self.addRoute() // step 2
}
func addRoute() {
var pointsToUse: [CLLocationCoordinate2D] = []
if PlaceLat != "" || PlaceLong != "" {
let coords = "\(PlaceLat), \(PlaceLong)"
let p = CGPointFromString(coords)
pointsToUse += [CLLocationCoordinate2DMake(CLLocationDegrees(p.x), CLLocationDegrees(p.y))]
}
pointsToUse += [CLLocationCoordinate2DMake(CLLocationDegrees(coords!.latitude), CLLocationDegrees(coords!.longitude))]
let myPolyline = MKPolyline(coordinates: &pointsToUse, count: 2)
mapView.addOverlay(myPolyline)
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.strokeColor = UIColor.greenColor()
return lineView // step 3
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.coords = manager.location!.coordinate
print("locations = \(coords!.latitude) \(coords!.longitude)")
}
My code is very disorderly because i mixed 4-5 tutorials. Also, these tutorials is written with swift 1.2.(i have tried to edit it to swift 2, but i am fail)
Did you ever resolve your problem? Using the latest iteration of Swift 2 in XCode 7.3, in your view (we will call it MyViewController):
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.mapView.delegate = self
var coordinates : [CLLocationCoordinate2D] = [];
addRoute(coordinates);
}
func addRoute(coordinates: [CLLocationCoordinate2D]) {
// insert your code to populate coordinates array with your coordinates
polyLine = MKPolyline(coordinates: &coordinates, count: coordinates.count)
self.mapView.addOverlay(polyLine, level: MKOverlayLevel.AboveRoads)
}
Then in the same file:
extension MyViewController: MKMapViewDelegate {
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let pr = MKPolylineRenderer(overlay: overlay);
pr.strokeColor = UIColor.blueColor().colorWithAlphaComponent(0.5);
pr.lineWidth = 5;
return pr;
}
}
You may find the important part was the extension. I haven't tested this code, so feel free to correct any issues that crept in.
in your CLLocationManagerDelegate delegate function didUpdateLocations you can update your location by setting
self.myLocation = locations[0] as CLLocation
Then call MakeRoute() - This is a function i wrote to either make a route by car or by walking (hence the self.driveIsSet)
func makeRoute() {
let startPlaceMark = MKPlacemark(coordinate: myLocation.coordinate)
let endPlaceMark = MKPlacemark(coordinate: restLocation.coordinate)
let startMapItem = MKMapItem(placemark: startPlaceMark)
let endMapItem = MKMapItem(placemark: endPlaceMark)
let directionRequest = MKDirectionsRequest()
directionRequest.source = startMapItem
directionRequest.destination = endMapItem
if self.driveIsSet {
directionRequest.transportType = .automobile
} else {
directionRequest.transportType = .walking
}
let directions = MKDirections(request: directionRequest)
directions.calculate { (routeResponse, routeError) in
guard let routeResponse = routeResponse else {
if let routeError = routeError {
print(routeError)
}
return
}
self.mapView.removeOverlays(self.mapView.overlays)
let route = routeResponse.routes[0]
self.mapView.add(route.polyline, level: .aboveRoads)
}
}

Saved Parse Database into an array called recipes and am now trying to access content within that array

I have a database in parse that i have pulled into a swift array. The custom parse object is called UserRecipe. The array is called recipes and is located in the viewDidLoad method. I am trying to set the imageview i have called recipeImage, to always access the image of the first element in the array. I do this in the updateImage function but am not sure if I have the correct syntax. Also the array seems to be stored only with the viewDidLoad method and is not accessible to my updateImage function. I'm wondering how to make it global so all functions can access it. Thanks in advance for any help.
The database looks like this:
import UIKit
import Parse
//import ParseFacebookUtilsV4
import FBSDKCoreKit
import FBSDKLoginKit
class ViewController: UIViewController {
let recipes = [PFObject]?.self
#IBOutlet var recipeImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
//load in all data from Parse custom Object UserRecipe and store it in variable recipes
var query = PFQuery(className:"UserRecipe")
query.findObjectsInBackgroundWithBlock {
(recipes: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let recipes = recipes {
for recipe in recipes {
print(recipe["recipeName"])
}
}
} else {
print("Error: \(error!) \(error!.userInfo)")
}
}
let gesture = UIPanGestureRecognizer(target: self, action: Selector("wasDragged:"))
recipeImage.addGestureRecognizer(gesture)
//let tapping = UITapGestureRecognizer(target: self, action: Selector("wasTapped:"))
//recipeImage.addGestureRecognizer(tapping)
recipeImage.userInteractionEnabled = true
//updateImage()
//getUserInfo()
}
func wasDragged(gesture: UIPanGestureRecognizer) {
//Dragging Animation
let translation = gesture.translationInView(self.view)
let imageDrag = gesture.view!
imageDrag.center = CGPoint(x: self.view.bounds.width / 2 + translation.x, y: self.view.bounds.height / 2 + translation.y - 153)
let xFromCenter = imageDrag.center.x - self.view.bounds.width / 2 + translation.x
let scale = min(100 / abs(xFromCenter), 1)
var rotation = CGAffineTransformMakeRotation(xFromCenter / 200)
var stretch = CGAffineTransformScale(rotation, scale, scale)
imageDrag.transform = stretch
//determines whether current user has accepted or rejected certain recipes
if gesture.state == UIGestureRecognizerState.Ended {
var acceptedOrRejected = ""
if imageDrag.center.x < 100 {
acceptedOrRejected = "rejected"
print("not chosen")
//print("not chosen" + object["recipeName"])
} else if imageDrag.center.x > self.view.bounds.width - 100 {
acceptedOrRejected = "accepted"
print("Chosen")
}
/*if acceptedOrRejected != "" {
PFUser.currentUser()?.addUniqueObjectsFromArray([displayedUserId], forKey: acceptedOrRejected)
PFUser.currentUser()?.saveInBackgroundWithBlock({
(succeeded: Bool, error: NSError?) -> Void in
if succeeded {
} else {
print(error)
}
})
}*/
//Resets image position after it has been let go of
rotation = CGAffineTransformMakeRotation(0)
stretch = CGAffineTransformScale(rotation, 1, 1)
imageDrag.transform = stretch
imageDrag.center = CGPoint(x: self.view.bounds.width / 2, y: self.view.bounds.height / 2 - 153)
updateImage()
}
}
func updateImage() {
recipeImage.image = recipes["image"][0]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am not across Parse but if I were you these are things I would try,
Set a breakpoint in
func updateImage() {
// set breakpoint here and check whether recipes contains any data.
recipeImage.image = recipes["image"][0]
}
Replace the code as shown below,
//replace
recipes["image"][0]
//to
recipes[0]["image"]

Swift- Set Visible menucontroller

I'm trying to display a UimenuController but I can not view it. how can I do?
let MenuController: UIMenuController = UIMenuController.sharedMenuController()
MenuController.menuVisible = true
MenuController.arrowDirection = UIMenuControllerArrowDirection.Down
MenuController.setTargetRect(CGRectMake(100, 80, 50, 50), inView: self.view)
let MenuItem_1: UIMenuItem = UIMenuItem(title: "Menu", action: "delete:")
let MenuItems: NSArray = [delete]
MenuController.menuItems = MenuItems
In order to actually have the menu to display you need to do the following:
Call becomeFirstResponder() before you get your sharedMenuController
Call menu.setMenuVisible(true, animated: true) at the end
Override the canBecomeFirstResponder function
Override the canPerformAction function
Write the function for the selector
Here is an example
func someFunc() {
becomeFirstResponder()
var menu = UIMenuController.sharedMenuController()
var deleteItem = UIMenuItem(title: "Delete me", action: Selector("deleteLine"))
menu.menuItems = [deleteItem]
menu.setTargetRect(CGRectMake(100, 80, 50, 50), inView: self)
menu.setMenuVisible(true, animated: true)
}
func deleteLine() {
//Do something here
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
// You need to only return true for the actions you want, otherwise you get the whole range of
// iOS actions. You can see this by just removing the if statement here.
if action == Selector("deleteLine") {
return true
}
return false
}
Swift 3 version code:
func someFunc() {
becomeFirstResponder()
var menu = UIMenuController.shared
var deleteItem = UIMenuItem(title: "Delete me", action: #selector(ViewController.deleteLine))
menu.menuItems = [deleteItem]
menu.setTargetRect(CGRect(x: 0.0, y: 0.0, width: 20, height: 20), in: self)
menu.setMenuVisible(true, animated: true)
}
func deleteLine() {
//Do something here
}
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
// You need to only return true for the actions you want, otherwise you get the whole range of
// iOS actions. You can see this by just removing the if statement here.
if action == #selector(ViewController.deleteLine) {
return true
}
return false
}
One more important thing is action for UIMenuItem should be implemented.

Resources