How can i Popup a NSMenu in a custom views in NSMenuItems? - swift

I have a NSMenu with a custom views, just like this.
ViewController.swift
class ViewController: NSViewController {
#IBOutlet var theMenu: NSMenu!
#IBOutlet var theMenuItem: NSMenuItem!
#IBOutlet var customView: NSView!
override func viewDidLoad() {
theMenuItem.view = customView
}
#IBAction func showTheMenuWithCustomView(_ sender: NSButton) {
// Popup the menu below button
let position = NSPoint(x: 0, y: sender.frame.height+2)
theMenu.popUp(positioning: nil, at: position, in: sender)
}
}
This works well.
But in custom views, i need to popup an another menu. After i call the popUp method, it just show nothing. Here is the code:
CustomView.swift
class CustomView: NSView {
#IBAction func showCustomViewMenu(_ sender: NSButton) {
// Create the menu in custom view
let customViewMenu = NSMenu()
// Add a menu item in it
customViewMenu.addItem(withTitle: "no one care", action: nil, keyEquivalent: "")
// Popup the menu below button
let position = NSPoint(x: 0, y: sender.frame.height+2)
customViewMenu.popUp(positioning: nil, at: position, in: sender)
}
}
You can clone this project to test it:
https://github.com/Caldis/NSMenuQuestion
How can i Popup NSMenu in custom views in NSMenuItems ?

Use this line to show the additional popup on CustomView:
NSMenu.popUpContextMenu(customViewMenu, with: NSApp.currentEvent!, for: sender)
Additionally, if you also need to disable other popups on whole CustomView, override rightMouseDown in CustomView, like so:
class CustomView: NSView {
override func rightMouseDown(with event: NSEvent) {
// Do nothing
}
}

Related

Global View visible in all .xib ViewControllers

I'm trying to create a "Global View" maybe it goes into AppDelegate, but I'm not sure. I would like my View to always be visible, no matter what ViewController .xib you are on in the app.
For now I have inserted the View in the first ViewController .xib but when I navigate in the other ViewControllers the View disappears.
This is the code:
import UIKit
class MonitoringViewController: UIViewController {
#IBOutlet var contentView: UIView!
#IBOutlet weak var viewForTab: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonTab1(_ sender: UIButton) {
}
#IBAction func buttonTab2(_ sender: UIButton) {
delegate?.navigateToAlertViewController()
let alert = AlertViewController(nibName: "AlertViewController", bundle: nil)
contentView.addSubview(alert.view)
alert.didMove(toParent: self)
}
#IBAction func buttonTab3(_ sender: UIButton) {
}
}

How to use “Look Up in Dictionary” in NSPopover Swift

I'm building my macOS app that contains NSPopover with NSTextView inside. If "Look up & data detectors" is active at system preferences, NSTextView will support this functionality without any additional settings. But in case, when you place NSTextView inside of NSPopover, this function won't work anymore. Is there any way to fix this problem without NSTextView.showDefenition(for: at:)?
How it usually works
Same thing, but won't work with NSTextView that placed inside of NSPopover
I've started new test project just to check behaviour, so code is simple as it is.
Main Controller:
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var popoverButton: NSButton!
#IBAction func pressButton(_ sender: NSButton) {
let popover = NSPopover()
popover.contentViewController = PopoverController()
popover.contentSize = CGSize(width: 470, height: 300)
popover.show(relativeTo: popoverButton.bounds, of: popoverButton, preferredEdge: NSRectEdge.minY)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Popover Controller:
import Cocoa
class PopoverController: NSViewController {
var textView = NSTextView()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear() {
super.viewWillAppear()
view.addSubview(textView)
textView.translatesAutoresizingMaskIntoConstraints = false
textView.frame = view.bounds
}
}

Can a Menu Bar Application switch between a popover or a menu?

I am writing a MacOS Menu Bar Application which uses a popover. I have relied on a number of tutorials to get things going. The application doesn’t use a storyboard or Interface Builder.
The plan is to present a menu on right-click or a popover on left-click.
Very briefly, the code looks something like this:
class AppDelegate: NSObject, NSApplicationDelegate {
var popover=NSPopover()
var statusBarItem: NSStatusItem!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Popover & Content View
let contentView = ContentView()
self.popover.contentViewController = NSHostingController(rootView: contentView)
// Menu
self.statusBarItem = NSStatusBar.system.statusItem(withLength: 18)
if let statusBarButton = self.statusBarItem.button {
statusBarButton.title = "☰"
statusBarButton.action = #selector(togglePopover(_:))
statusBarButton.sendAction(on: [.leftMouseUp, .rightMouseUp])
}
}
#objc func togglePopover(_ sender: AnyObject?) {
let statusBarButton=self.statusBarItem.button!
let event = NSApp.currentEvent!
event.type == NSEvent.EventType.rightMouseUp ? print("Right-Click") : print("Left-Click")
func show(_ sender: AnyObject) {
self.popover.show(relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: NSRectEdge.maxY)
}
func hide(_ sender: AnyObject) {
popover.performClose(sender)
}
self.popover.isShown ? hide(sender as AnyObject) : show(sender as AnyObject)
}
}
I can get the popover working, but how would I like to show a different real menu when the button is right-clicked. How do I get this going?

How to add button target function in IBAction method

I am using SWRevealViewController for side menu. while i click on menuBtn, im performing right toggle operation. I want the same when i swipe right Can any one Please Help me with it.
class Invoice: UIViewController {
#IBOutlet weak var menuBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let revealViewController = self.revealViewController()
menuBtn.addTarget(revealViewController, action: #selector(SWRevealViewController.rightRevealToggle(_:)), for: .touchUpInside)
}
#IBAction func swipeGesture(_ sender: UISwipeGestureRecognizer) {
print("Swiped")
//I want same action as i clicked on mentBtn
}
}
Something like this should work:
#IBAction func swipeGesture(_ sender: UISwipeGestureRecognizer) {
self.revealViewController().rightRevealToggle(self.menuBtn)
}

Move view using Scroll View to prevent keyboard from obscuring text field or text view

I am implementing an app where tapping a button will pop up a location entry screen which is implemented with Modally presented Segue and Visual Effect with Blur to make the entry window appear hovering above the main screen.
The pop up screen has a text field for location name and a multi line text view for remarks.
Problem is the multi line text view is partially obscured by the keyboard on smaller devices and in landscape view.
Landscape View
Portrait View
Modal Segue View
I tried to utiize the solution described here, which is supposed to utilize scroll view to shift the view when keyboard appears, but it doesn't seem to work for my project: Move a view up only when the keyboard covers an input field
This question doesn't answer my problem as my text field and view are inside another view to make it appear like a popup to the main screen: Move a view up only when the keyboard covers an input field
In order to implement the pop up view (in a modal segue) I placed the text field and view inside a View which is placed inside a Scroll View which is located inside the view of the Visual Effects with Blur View.
Another problem I'm experiencing is I impleneted the following code to dismiss keyboard when tapping outside the text field/view, but for some reason it doesn't seem to work
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
self.view.endEditing(true)
}
Here is the code for the View Controller of the pop up view in Xcode 9.2
Archive of the project: Xcode Project Archive
//
// LocationEntryScrollViewController.swift
//
import UIKit
class LocationEntryScrollViewController: UIViewController, UITextViewDelegate, UITextFieldDelegate {
#IBOutlet weak var ScrollView: UIScrollView!
#IBOutlet weak var LocationEntryView: UIView!
#IBOutlet weak var LocationTextField: UITextField!
#IBOutlet weak var RemarksTextView: UITextView!
var activeTextField: UITextField?
var activeTextView: UITextView?
override func viewDidLoad()
{
super.viewDidLoad()
registerForKeyboardNotifications()
LocationEntryView.layer.cornerRadius = 10
LocationEntryView.layer.masksToBounds = true
LocationTextField.delegate = self // dismiss keyboard when tapped outside keyboard
RemarksTextView.delegate = self // dismiss keyboard when tapped outside keyboard
}
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
LocationTextField.becomeFirstResponder()
RemarksTextView.becomeFirstResponder()
}
/**
* Called when the user click on the view (outside the UITextField).
*/
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
self.view.endEditing(true)
}
override func viewDidDisappear(_ animated: Bool)
{
deregisterFromKeyboardNotifications()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func DoneButtonTapped(_ sender: Any)
{
dismiss(animated: true, completion: nil)
}
#IBAction func CancelButtonTapped(_ sender: Any)
{
}
#objc func keyboardWasShown(notification: NSNotification)
{
//Need to calculate keyboard exact size due to Apple suggestions
self.ScrollView.isScrollEnabled = true
var info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)
self.ScrollView.contentInset = contentInsets
self.ScrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeTextField = self.activeTextField
{
if (!aRect.contains(activeTextField.frame.origin))
{
self.ScrollView.scrollRectToVisible(activeTextField.frame, animated: true)
}
}
if let activeTextView = self.activeTextView
{
if (!aRect.contains(activeTextView.frame.origin))
{
self.ScrollView.scrollRectToVisible(activeTextView.frame, animated: true)
}
}
}
#objc func keyboardWillBeHidden(notification: NSNotification)
{
//Once keyboard disappears, restore original positions
let info : NSDictionary = notification.userInfo! as NSDictionary
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
self.ScrollView.contentInset = contentInsets
self.ScrollView.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
self.ScrollView.isScrollEnabled = false
}
func textFieldDidBeginEditing(_ textField: UITextField)
{
activeTextField = textField
}
func textFieldDidEndEditing(_ textField: UITextField)
{
activeTextField = nil
}
func textViewDidBeginEditing(_ textView: UITextView)
{
activeTextView = textView
}
func textViewDidEndEditing(_ textView: UITextView) {
activeTextView = nil
}
func registerForKeyboardNotifications()
{
//Adding notifies on keyboard appearing
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func deregisterFromKeyboardNotifications()
{
//Removing notifies on keyboard appearing
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
}