we can disable and enable user interaction in ios app using
UIApplication.shared.beginIgnoringInteractionEvents()
UIApplication.shared.endIgnoringInteractionEvents()
how to disable MAC App user interaction? we can disable interaction of a view but not the whole app. is there any solution?
You may disable it using hitTest.
class CAInteractionView: NSView {
var isUSerIntactionEnable:Bool = true
override func hitTest(_ point: NSPoint) -> NSView? {
if(self.isUSerIntactionEnable) {
return super.hitTest(point)
}
return nil
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
}
Call it like
if let cView = self.view as? CAInteractionView {
cView.isUSerIntactionEnable = false
}
Related
What I intend to do is validate four NSTextFields, I will try to make sure that all of them are filled correctly, and completely.
textField and alert
As the picture shows: if a user types anything in the 1st NSTextField, types something wrong or leaves it blank in the 2nd NSTextField, press Enter or click outside the boundary, it should show an alert, and try to stop the user from resigning from the 2nd NSTextField, and he should confirm his input in 2nd NSTextField.
What I have tried and the problems I faced:
Use textFieldShouldEndEditing(_:)
When I type something. Wrong, show an alert and return false. It runs well.
But if I type nothing, textFieldShouldEndEditing(_:) will not invoke. (Because it won't be triggered without typing)
func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool {
if control.stringValue != validInput{
showAlert()
return false
}else{
return true
}
}// plz ignore the delegation
CustomNSTextField & resignFirstResponder()
When the user clicked NSTextField, NSTextField would becomeFirstResponder(), followed by ResignFirstResponder() because the FieldEditor would enter. So it is invalid to monitor resignFirstResponder().
Use textFieldShouldEndEditing(_:)
When I type something wrong or type nothing, press Enter or click outside the boundary, show an alert, but it is difficult to stop the following functions: for example, if I click the button "open", it may show an alert and an openFile dialog in the meantime.
What may solve my problems:
Whether there is a trick that invokes textFieldDidBeginEditing (_:) when NSTextField resignFirstResponder() ?
Whether there is a function that monitors the window's current fieldEditor resignFirstResponder(), and it can return false while I want the user to confirm his input.
I found it NOT as difficult as I thought to write a custom fieldEditor. Follow the tutorial here. I post the essential part of my code here for anyone who is facing similar problems.
class CustomFieldCell: NSTextFieldCell {
static var durationFieldEditor: CustomFieldEditor = {
let customFieldEditor = CustomFieldEditor()
return fieldEditor
}()
override func fieldEditor(for controlView: NSView) -> NSTextView? {
return Self.customFieldEditor
}
}
class CustomFieldEditor: NSTextView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
commonInit()
}
override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) {
super.init(frame: frameRect, textContainer: container)
commonInit()
}
override func becomeFirstResponder() -> Bool {
// condition
return super.becomeFirstResponder()
}
override func resignFirstResponder() -> Bool {
// condition
return super.resignFirstResponder()
}
private func commonInit() {
isFieldEditor = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Currently I have a little issue when it comes to buttons(NSButton) which have a tracking area and views(NSView overlay) above these buttons, this is my setup:
Custom button:
class AppButton: NSButton {
override func updateTrackingAreas() {
super.updateTrackingAreas()
let area = NSTrackingArea(
rect: self.bounds,
options: [.mouseEnteredAndExited, .activeAlways],
owner: self,
userInfo: nil
)
self.addTrackingArea(area)
}
override func mouseEntered(with event: NSEvent) {
NSCursor.pointingHand.set()
}
override func mouseExited(with event: NSEvent) {
NSCursor.arrow.set()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
An instance of this button class is used i a very basic NSView.
When I hover over the button, the cursor changes correctly.
When I click the button a new overlay(NSView) is opened above the button...
This is where the problem starts:
When I hover over the overlay where my button is placed, the cursor still changes...
I did not know that a NSTrackingArea is going through all views..
How can i solve this issue?
Can I set any property on the overlay(NSView) to somehow disable the NSTrackingArea on the button?
Thanks!!
You can subclass NSView and add local monitoring for events. Check if the event has occurred over the view and if true return nil. This will avoid propagating the events being monitored. If the event is outside the view frame you can propagate it normally returning the event monitored.
class CustomView: NSView {
override func viewWillMove(toSuperview newSuperview: NSView?) {
super.viewWillMove(toSuperview: newSuperview)
wantsLayer = true
layer?.backgroundColor = NSColor.windowBackgroundColor.cgColor
layer?.borderWidth = 1
NSEvent.addLocalMonitorForEvents(matching: [.mouseEntered, .mouseExited, .leftMouseDown]) { event in
if self.frame.contains(event.locationInWindow) {
// if cursor is over the view just return nil to do not propagate the events
return nil
}
return event
}
}
}
If you are trying to stop mouse events from a viewController, add this code in viewDidLoad
NSEvent.addLocalMonitorForEvents(matching: [.mouseEntered, .mouseExited, .leftMouseDown]) { event in
if self.view.frame.contains(event.locationInWindow) {
return nil
}
return event
}
I am writing a macOS application with Swift using story boards. I have a NSTableView which contains files that I want the user to be able to preview via QuickLook.
I seemingly have everything in place and my code looks very similar to what has been described here: QuickLook consumer as a delegate from an NSViewController, but I keep getting the error
-[QLPreviewPanel setDataSource:] called while the panel has no controller - Fix this or this will raise soon.
See comments in QLPreviewPanel.h for -acceptsPreviewPanelControl:/-beginPreviewPanelControl:/-endPreviewPanelControl:.
I've been trying to adapt the solution of above post to my situation with Swift and story boards.
The main pieces are:
import Quartz
class ViewController: NSViewController, QLPreviewPanelDataSource, QLPreviewPanelDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let windowNextResponder = self.view.window?.nextResponder
self.view.window?.nextResponder = self
self.nextResponder = windowNextResponder
}
// *** Quicklook stuff ***
#IBAction func quickLookButtonAction(_ sender: Any) {
guard qlPanel != nil else {
return
}
if qlPanel!.currentController == nil {
print ("No controller")
//qlPanel!.windowController = self.view.window?.windowController
// qlPanel!.updateController()
} else {
print (qlPanel!.currentController)
}
qlPanel!.delegate = self
qlPanel!.dataSource = self
qlPanel!.makeKeyAndOrderFront(self)
}
func numberOfPreviewItems(in panel: QLPreviewPanel!) -> Int {
return CSVarrayController.selectedObjects.count
}
func previewPanel(_ panel: QLPreviewPanel!, previewItemAt index: Int) -> QLPreviewItem! {
let file = CSVarrayController.selectedObjects[index] as! CSVfile
return file.url as NSURL
}
override func acceptsPreviewPanelControl(_ panel: QLPreviewPanel!) -> Bool {
return true
}
override func beginPreviewPanelControl(_ panel: QLPreviewPanel!) {
panel.dataSource = self
panel.delegate = self
}
override func endPreviewPanelControl(_ panel: QLPreviewPanel!) {
panel.dataSource = nil
panel.delegate = nil
}
}
With or without messing with the responder chain I get the error.
The delegate functions all get called as expected as well.
Remove
qlPanel!.delegate = self
qlPanel!.dataSource = self
in quickLookButtonAction, the viewcontroller isn't in control yet. Wait for beginPreviewPanelControl.
From the documentation for currentController:
You should never change the preview panel’s state (its delegate, datasource, and so on) if you are not controlling it.
From comments in QLPreviewPanel.h for -beginPreviewPanelControl::
Sent to the object taking control of the Preview Panel.
The receiver should setup the preview panel (data source, delegate, binding, etc.) here.
Background
I have an app that uses AVFoundation in order to have a custom camera. This happens in the OCRViewController. When I take a picture I send the captured picture to a different view ImagePreviewViewController.
I am using Xcode 10.2.1 (10E1001) with Swift 5
The Goal
What I would like to achieve is to lock the orientation of the ImagePreviewViewController to the original orientation of the image. I already know how to get the orientation of the image but I am not able to lock the orientation of the view.
I get the image rotation as such: let imageOri = capturedImage?.imageOrientation
What did I try?
I tried the accepted answers at and several other sources:
How to lock orientation just for one view controller?
How to lock orientation of one view controller to portrait mode only in Swift
https://www.hackingwithswift.com/example-code/uikit/how-to-lock-a-view-controllers-orientation-using-supportedinterfaceorientations
Reading the documentation at https://developer.apple.com/documentation/uikit/uiviewcontroller#//apple_ref/occ/instm/UIViewController/supportedInterfaceOrientations under Handling View Rotation the following is stated:
I also tried the many suggested solutions while writing this query, however, the majority appears to use the following approach (or a variation of it), and it does not work for me.
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
override func shouldAutorotate() -> Bool{
return false
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.Portrait
}
As of iOS 8, all rotation-related methods are deprecated. Instead, rotations are treated as a change in the size of the view controller’s view and are therefore reported using the viewWillTransition(to:with:) method.
However, I am not sure how to progress from here.
Interesting code snippets
The following method is in my OCRViewController, here I instantiate the ImagePreviewViewController and attach the captured image.
func displayCapturedPhoto(capturedPhoto : UIImage) {
let imagePreviewViewController = storyboard?.instantiateViewController(withIdentifier: "ImagePreviewViewController") as! ImagePreviewViewController
imagePreviewViewController.capturedImage = capturedPhoto
navigationController?.pushViewController(imagePreviewViewController, animated: true)
}
Using the below override function inside my ImagePreviewViewController I am able to detect the orientation of the view controller.
override func viewDidAppear(_ animated: Bool) {
if UIDevice.current.orientation.isLandscape {
print("Landscape")
} else {
print("Portrait")
}
}
To restrict the rotation of one screen, use this.
In AppDelegate
var restrictRotation = Bool()
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if !restrictRotation {
return .portrait
} else {
return .all
}
}
In your viewcontroller add the function,
func restrictRotation(restrict : Bool) -> Void {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
appDelegate?.restrictRotation = restrict
}
In the ViewDidload() method, call the function to disable rotation.
self.restrictRotation(restrict: false)
in viewWillDisappear() method, call the function to enable rotation.
self.restrictRotation(restrict: true)
When I try and scan a card with Card.IO. it does not bring up the camera let alone the green lines around it. When I run this code and press the button it does not show the camera in the view controller. Any ideas ? The sample works fine in io8 in obj-C .
#IBAction func PressScan(sender: AnyObject) {
var CardScanner : CardIOPaymentViewController = CardIOPaymentViewController()
CardScanner.modalPresentationStyle = UIModalPresentationStyle.FullScreen
self.presentViewController(CardScanner, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
CardIOPaymentViewController.preload()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
}
func userDidCancelPaymentViewController(paymentViewController: CardIOPaymentViewController!) {
}
Is there something in the plist file I need to amend to allow camera access for iOS8 ?
Solution is here: https://github.com/card-io/card.io-iOS-SDK/issues/76#issuecomment-65856792
Namely:
You are not initializing CardIOPaymentViewController correctly. It should be
var CardScanner : CardIOPaymentViewController = CardIOPaymentViewController(paymentDelegate: self)