Accessing Button Target Throwing Error - uikit

With the code below I get an error when I press the button. I'm not sure why.The error is unrecognized selector sent to instance and terminates with "terminating with uncaught exception of type NSException." Any help sure is appreciated.
Thanks
class WordSearchWindow: UIViewController {
let boardsize = 10
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(false)
buttonGridder()
}
func buttonGridder() {
for x in 0..<self.boardsize {
for y in 0..<self.boardsize{
let sizer = ButtonGrid.frame.width
let buttonSize:CGFloat = sizer / CGFloat(self.boardsize)
let letterButton = WordButton(column: x, row: y, buttonSize: buttonSize, buttonMargin: 0)
letterButton.addTarget(self, action: "letterFirst", forControlEvents: .TouchDown)
letterButton.addTarget(self, action: "letterSecond", forControlEvents: .TouchUpInside)
self.ButtonGrid.addSubview(letterButton)
}
}
}
func letterFirst(sender: WordButton) {
print("hello1")
}
func letterSecond(sender: WordButton) {
print("hello2")
}
}

Try this :
letterButton.addTarget(self, action: "letterFirst:", forControlEvents: .TouchDown)
letterButton.addTarget(self, action: "letterSecond:", forControlEvents: .TouchUpInside)
Add : in the action

Related

Can't get variable from Notification?

I want to write a project has a notification can print data which stored in main viewcontroller, but when notification fired, always get empty data.
In the ViewController
arrPerson = [String]()
override func viewDidLoad() {
super.viewDidLoad()
arrPerson.append("Peter")
arrPerson.append("Jack")
setNotification()
}
func printTheName() {
print("In printTheName")
print("arrPerson:\(arrPerson.count)")
for x in arrPerson {
print(x)
}
}
func setNotification() {
let notification = UNMutableNotificationContent()
notification.title = "title"
notification.subtitle = ""
notification.body = "body"
notification.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
let request = UNNotificationRequest(identifier: "check", content: notification, trigger: trigger)
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.add(request) { (error) in
if error != nil {
print("notificationCenter.add ERROR:\(error)")
// Handle any errors.
}
}
}
In the Appdelegate
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("In userNotificationCenter")
ViewController().printTheName()
}
The code will print out "userNotificationCenter","In printTheName","arrPerson:0".
I need get data from notification. Why arrPerson.count is 0 when call printTheName from notification?
Try to programatically instantiate the viewController using a window in your appdelegate.
You will be able to fetch the count then in your notification center.Or since your viewController class is the landing screen, Call the userNotifationCenter method in your main Class and not here.

Barcode on swift 4

I'm trying to upgrade mi app to swift 4, but the barcode reader is not working.
I have isolated the barcode reader code, and still not working. The camera works but it does not detect the barcode.
The code worked just fine on swift 3 iOS 10.
This is the complete code
import AVFoundation
import UIKit
class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.black
captureSession = AVCaptureSession()
let videoCaptureDevice = AVCaptureDevice.default(for: AVMediaType.video)
let videoInput: AVCaptureDeviceInput
do {
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice!)
} catch {
return
}
if (captureSession.canAddInput(videoInput)) {
captureSession.addInput(videoInput)
} else {
failed();
return;
}
let metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.ean8, AVMetadataObject.ObjectType.ean13, AVMetadataObject.ObjectType.pdf417]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession);
previewLayer.frame = view.layer.bounds;
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill;
view.layer.addSublayer(previewLayer);
captureSession.startRunning();
}
func failed() {
let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
captureSession = nil
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if (captureSession?.isRunning == false) {
captureSession.startRunning();
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if (captureSession?.isRunning == true) {
captureSession.stopRunning();
}
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject;
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: readableObject.stringValue!);
}
dismiss(animated: true)
}
func found(code: String) {
print(code)
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
I am using iOS 11 on my iPhone, upgraded to beta 9.
Any idea? Thank you.
I figured it out but Apple didn't make it so obvious. The callback function from the delegate AVCaptureMetadataOutputObjectsDelegate has been renamed and the parameter names are different!
So, replace
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
to
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
My view controller is now scanning QR Codes as before after this. It has the same parameters but the first parameter name is different. Change the function and parameter names and build/run.
After changing the delegate call back :
From
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
To
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
I need to set all available types for metadataObjectTypes too as below-
output.metadataObjectTypes=output.availableMetadataObjectTypes
After changing your code from:
func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {}
to:
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {}
everything works again.
You can use QRCodeScanner83 to scan barcodes:
import QRCodeScanner83
import AVFoundation
...
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "CodeScannerViewController") as? CodeScannerViewController else {
return
}
vc.callbackCodeScanned = { code in
print("SCANNED CODE: \(code)")
vc.dismiss(animated: true, completion: nil)
}
self.present(vc, animated: true, completion: nil)
If you need custom UI, then you can nest from CodeScannerViewController and set CodeScannerViewController.delegate to receive updates of the scanner state.

Unrecognized selector sent to instance when using UIPanGesture

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.

You need to specify a parseClassName for the PFQueryTableViewController

I created a tableViewcontroller and assigned it the custom class: PFQueryTableViewController in story board. I then also gave it the parseClassName "userMessage" and for some reason when i try to run the application I always get the same error message: NSInternalInconsistencyException', reason: 'You need to specify a parseClassName for the PFQueryTableViewController.
I dont understand why I am getting this error because I explicitly gave the class a parseClassName.
Here is my associated code for the PFQueryTabletableViewController:
import UIKit
import CoreLocation
import Parse
class TableViewController: PFQueryTableViewController, CLLocationManagerDelegate {
let userMessages = ["blah blahh blahhh", "Beep Beep Boop", "Beep Beep Bobbity boop"]
let locationManager = CLLocationManager()
var currLocation: CLLocationCoordinate2D?
override init!(style: UITableViewStyle, className: String!) {
super.init(style: style, className: className)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.parseClassName = "userMessage"
self.textKey = "text"
self.pullToRefreshEnabled = true
self.objectsPerPage = 40
}
private func alert(message: String){
let alert = UIAlertController(title: "Uh-OH", message: message, preferredStyle: UIAlertControllerStyle.Alert)
let action = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil)
let cancel = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
let settings = UIAlertAction(title: "Settings", style: UIAlertActionStyle.Default) {(action) -> Void in
UIApplication.sharedApplication().openURL(NSURL(string: UIApplicationOpenSettingsURLString)!)
return
}
alert.addAction(settings)
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 120
self.tableView.rowHeight = 120
locationManager.desiredAccuracy = 100
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
alert("Cannot fetch your location!!")
}
override func queryForTable() -> PFQuery! {
let query = PFQuery(className: "Messages")
if let queryLoc = currLocation {
query.whereKey("location", nearGeoPoint: PFGeoPoint(latitude: queryLoc.latitude, longitude: queryLoc.longitude), withinMiles: 1)
query.limit = 40
query.orderByDescending("createdAt")
}else {
query.whereKey("location", nearGeoPoint: PFGeoPoint(latitude: 37.41182, longitude: -121.941125), withinMiles: 1)
query.limit = 40
query.orderByDescending("createdAt")
}
return query
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
locationManager.stopUpdatingLocation()
if(locations.count > 0) {
let location = locations[0] as CLLocation
println(location.coordinate)
currLocation = location.coordinate
} else {
alert("Cannot fetch your loation")
}
}
override func objectAtIndexPath(indexPath: NSIndexPath!) -> PFObject! {
var obj : PFObject? = nil
if(indexPath.row < self.objects.count) {
obj = self.objects[indexPath.row] as? PFObject
}
return obj
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userMessages.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath!, object: PFObject!) -> PFTableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TableViewCell
cell.messageText.text = object.valueForKey("text") as? String
cell.messageText.numberOfLines = 0
let views = object.valueForKey("count") as Int
cell.numberOfViewsLabel.text = "\(views)"
cell.numberOfViewsLabel.text = "\((indexPath.row + 1) * 5)"
return cell
}
func addToViews(sender: AnyObject) {
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
let object = objectAtIndexPath(hitIndex)
object.incrementKey("count")
object.saveInBackgroundWithBlock { (Bool, NSError) -> Void in
//blahhh
}
self.tableView.reloadData()
}
}
`
parseClassName is a readonly variable and is only used when subclassing PFObject.
https://parse.com/docs/ios/api/Classes/PFObject.html#//api/name/parseClassName
The class name of the object.
#property (strong, readonly) NSString *parseClassName
Declared In
PFObject.h
Obj-C
#implementation MYGame
#dynamic title;
+ (NSString *)parseClassName {
return #"Game";
}
#end
Swift
class MYGame: PFObject {
class func parseClassName() -> String! {
return "Game"
}
}
In my case I had used a storyboard and needed to create an initWithCoder: method in my PFQueryTableViewController subclass. The template pointed to in the Parse.com docs lacks this method, but the first comment following the example does include an example implementation: https://gist.github.com/jamesyu/ba03c1a550f14f88f95d#gistcomment-74202
The message "You need to specify a parseClassName for the PFQueryTableViewController" is being generated because none of the methods are setting the PFQueryTableViewController's parseClassName property. You'll note that the property is defined quite plainly in the initWithStyle: method example provided in the docs. But, that method won't be called if the view is loaded via a storyboard: for that you'll need to set parseClassName in the initWithCoder: method.
Also, don't confuse subclassing a PFQueryTableViewController for a PFObject. For a PFObject you need to create a class method called parseClassName and also register the subclass before calling [Parse setApplicationId:aid clientKey:ckey]. You don't do those things for a PFQueryTableViewController or any of the other ParseUI view controllers. They rely on one or more of the init methods.

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