I am writing a popover menu bar app in OS X.
The goal is to copy the selected text of the currently active application (not my popover) into my app so I can use it as a String.
The NSPasteboard class is used to put/get info on the pasteboard. As I understand it, you want to get the currently selected text in another application into a string in your application. The Accessibility APIs to achieve this.
You can send keys to another application, so you could send Cmd-C to the other application, and then pull the data from the pasteboard. An example of this in obj-c can be found here.
Figured it out!
NOTE: You have to delay the paste function. copyText() needs time to write to the pasteboard.
func copyText() {
// Clear pasteboard
pasteBoard.clearContents()
let src = CGEventSourceCreate(CGEventSourceStateID.HIDSystemState)
//let cmdd = CGEventCreateKeyboardEvent(src, 0x37, true)
let cmdu = CGEventCreateKeyboardEvent(src, 0x37, false)
let c_down = CGEventCreateKeyboardEvent(src, 0x08, true)
let c_up = CGEventCreateKeyboardEvent(src, 0x08, false)
// Set Flags
CGEventSetFlags(c_down, CGEventFlags.MaskCommand)
CGEventSetFlags(c_up, CGEventFlags.MaskCommand)
let loc = CGEventTapLocation.CGHIDEventTap
//CGEventPost(loc, cmdd)
CGEventPost(loc, c_down)
CGEventPost(loc, c_up)
CGEventPost(loc, cmdu)
}
func paste() -> String {
let lengthOfPasteboard = pasteBoard.pasteboardItems!.count
print(lengthOfPasteboard)
var theText = ""
if lengthOfPasteboard > 0 {
theText = pasteBoard.pasteboardItems![0].stringForType("public.utf8-plain-text")!
} else {
theText = "Nothing Coppied"
}
//print(theText)
return theText
}
I'm calling this from AppDelegate.swift, not the ViewController. So that it will hopefully copy the text before my popover becomes the active/focused window.
Related
I am thinking about porting my macOS app to Catalyst.
My app shows a transparent window (no title bar, clear background) on top of all other apps windows (dock included).
To do that, in the non-catalyst code I use:
window.isOpaque = false
window.hasShadow = false
window.backgroundColor = .clear
window.styleMask = .borderless
window.isMovableByWindowBackground = true
window.level = .statusBar
Using UIKit, I was only able to remove the toolbar so far:
window.titleBar.titleVisibility
...But no clue about the other settings.
I plan to make the app available on the App Store in the future, but if the only way to do so is and hack with a private API, that's fine.
Any ideas?
Thanks in advance
There is no official API for doing that, but you can easily access the NSWindow instance and modify it directly. You can do that manually or using some library like Dynamic (Full disclosure: I'm the author):
let window = Dynamic.NSApplication.sharedApplication.delegate.hostWindowForUIWindow(uiWindow)
window.isOpaque = false
window.hasShadow = false
window.backgroundColor = Dynamic.NSColor.clearColor
window.styleMask = 0 /*borderless*/
window.isMovableByWindowBackground = true
window.level = 25 /*statusBar*/
I have some success removing the close button on Catalyst by calling a function from the viewDidAppear(). I called it AppDelegate().disableTitleBarButtons(). Has to be from view did appear.
AppDelegate().disableTitleBarButtons() is as follows
func disableTitleBarButtons() {
func bitSet(_ bits: [Int]) -> UInt {
return bits.reduce(0) { $0 | (1 << $1) }
}
func property(_ property: String, object: NSObject, set: [Int], clear: [Int]) {
if let value = object.value(forKey: property) as? UInt {
object.setValue((value & ~bitSet(clear)) | bitSet(set), forKey: property)
}
}
// disable full-screen button
if let NSApplication = NSClassFromString("NSApplication") as? NSObject.Type,
let sharedApplication = NSApplication.value(forKeyPath: "sharedApplication") as? NSObject,
let windows = sharedApplication.value(forKeyPath: "windows") as? [NSObject]
{
for window in windows {
let resizable = 4
property("styleMask", object: window, set: [], clear: [resizable])
let fullScreenPrimary = 7
let fullScreenAuxiliary = 8
let fullScreenNone = 9
property("collectionBehavior", object: window, set: [fullScreenNone], clear: [fullScreenPrimary, fullScreenAuxiliary])
}
}
}
Where is says let resizable = 4,
Change to 3 for no Maximise,
Change to 2 for No minimise,
Change to 1 of No Close button.
Play with the other numbers or stylemask settings also. Good luck
I have an app which displays a PDFView, and I want it to print the PDF file from the view. Also, I want the Print panel to show the Page Setup Accessory (i.e. the Paper Size, Orientation and Scale settings, like in Preview, which appear as one panel of the drop-down list of options).
Currently, I'm mistakenly printing the PDFView, not the PDF document itself. This only gives me one page and includes the scrollbars in the print-out! I can't see how to init an NSPrintOperation referencing a PDFDocument rather than the PDFView.
Here's my code, which works, but isn't what I want. I presume I'll have to override either the printDocument or printOperation functions of NSDocument with similar code that defines the Panel and the Info.
func thePrintInfo() -> NSPrintInfo {
let thePrintInfo = NSPrintInfo()
thePrintInfo.horizontalPagination = .automatic // Tried fit
thePrintInfo.verticalPagination = .automatic // Tried fit
thePrintInfo.isHorizontallyCentered = true // Tried false
thePrintInfo.isVerticallyCentered = true // Tried false
thePrintInfo.leftMargin = 0.0
thePrintInfo.rightMargin = 0.0
thePrintInfo.topMargin = 0.0
thePrintInfo.bottomMargin = 0.0
thePrintInfo.jobDisposition = .spool
return thePrintInfo
}
// Need to show the 'Page Setup' Options as an Accessory
// e.g. Paper size, orientation.
#IBAction func printContent(_ sender: Any) {
let printOperation = NSPrintOperation(view: thePDFView, printInfo: thePrintInfo())
let printPanel = NSPrintPanel()
printPanel.options = [
NSPrintPanel.Options.showsCopies,
NSPrintPanel.Options.showsPrintSelection,
NSPrintPanel.Options.showsPageSetupAccessory,
NSPrintPanel.Options.showsPreview
]
printOperation.printPanel = printPanel
printOperation.run()
}
Based on #Willeke's comments, I've come up with the following, which seems to work well. (Minor quibble is that the Print dialog isn't a sheet.) If anyone has any improvements, please post a better answer.
#IBAction func printContent(_ sender: Any) {
let printOperation = thePDFView.document?.printOperation(for: thePrintInfo(), scalingMode: .pageScaleNone, autoRotate: true)
printOperation?.printPanel = thePrintPanel()
printOperation?.run()
}
I am a new iOS programming. Now i am creating a sample app which display text using ReadMoreTextView library. My content may contain many lines but by using this library i can maximumNumberOfLines to display how many lines of content should be displayed. I implement those content in cell of UITableView and i have problem is that, when i use label.attributedReadMoreText = NSAttributedString(string: "...") then end of content will display ... and when i click on it and then whole content will be display so, my question is that: How to not letting user click on that ... because i want user to click on cell then i will show another view and display whole content there?
How can i achieve something like this? Thank in advance.
This is how i set UITextView
lazy var categoryShortDetailLabel: ReadMoreTextView = {
let label = ReadMoreTextView()
label.font = UIFont(name: "SFCompactText-Regular", size: 16)
label.textColor = .black
label.isEditable = false
label.isSelectable = false
label.maximumNumberOfLines = 3
label.shouldTrim = true
label.attributedReadMoreText = NSAttributedString(string: "...")
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
Looking at the code here, I found that, the ReadMoreTextView is meant to provide you the feature like, ReadMore and ReadLess for the larger texts in textView.
However your requirement is to stop that functionality. Now, if you take a look at the code here, you will get the idea, that the function shoreMoreText and it's a private function so, can't override it. and this function is expanding the texts and setting the numberOfLines to zero. so, what you can do is, comment the code within and return from function to stop doing the action. Also as the ReadMoreTextView is Licensed as MIT(Read licence here) so, it's okay to modify the code.
private func showMoreText() {
return
/*if let readLessText = readLessText, text.hasSuffix(readLessText) { return }
shouldTrim = false
textContainer.maximumNumberOfLines = 0
if let originalAttributedText = _originalAttributedText?.mutableCopy() as? NSMutableAttributedString {
attributedText = _originalAttributedText
let range = NSRange(location: 0, length: text.count)
if let attributedReadLessText = attributedReadLessText {
originalAttributedText.append(attributedReadLessText)
}
textStorage.replaceCharacters(in: range, with: originalAttributedText)
}
invalidateIntrinsicContentSize()
invokeOnSizeChangeIfNeeded()*/
}
Try and share your results.
Hope it helps!
I have an issue with a OSX application. I want it to display an image, so I create an NSImageView object, and linked it to my view controller. I then ceate a function that should load an image from a selected folder. I get the image url correctly an dtry to load the image onto the NSImageView but that doesn't work (with no rpeorted error). The image is a tiff file, and the isValid variable return false it seems...
imageAve is the name of the NSImageView outlet :
#IBOutlet weak var imageAve: NSImageView!
Here the code of the function in question :
func loadImage() { //load the tiff file
let image_url = String(describing: self.loadedtraces.tracefilesurls[self.indexSelected])
let image_url_cut = String(image_url[image_url.index(image_url.startIndex, offsetBy: 7)...image_url.index(image_url.endIndex, offsetBy: -8)] + "_ave_merged.tif")
if FileManager.default.fileExists(atPath: image_url_cut) { //test if the file exists and that works fine.
let image_average = NSImage(byReferencingFile: image_url_cut)
if (image_average?.isValid == false) {
self.alert.informativeText = "The image is invalid."
self.alert.alertStyle = .informational
self.alert.icon = alert_info_logo
self.alert.beginSheetModal(for: self.view.window!)
} else {
self.imageAve.needsDisplay = true
self.imageAve.image = image_average
}
} else {
self.alert.informativeText = "The average merge image file does not exist in the traces folder."
self.alert.alertStyle = .informational
self.alert.icon = alert_info_logo
self.alert.beginSheetModal(for: self.view.window!)
}
}
Thanks to everyone who might able to help !
I created a simple timer which updates a counter within a NSMenuItem.
func timerUpdate(){
dispatch_async(dispatch_get_main_queue()){
// calculating time
let timeLeft = self.lxTimeLeft() // returns a double of remaining seconds
let hoursLeft = Int(timeLeft/3600)
let minLeft = Int(timeLeft/60) - (hoursLeft*60)
let secLeft = Int(timeLeft) - (hoursLeft*3600) - (minLeft*60)
let hoursLeftStr = String(format:"%02d", hoursLeft)
let minLeftStr = String(format:"%02d", minLeft)
let secLeftStr = String(format:"%02d", secLeft)
println("timeLeft \(hoursLeftStr):\(minLeftStr):\(secLeftStr)")
self.menuActiveFor.title = "\(self.activatedFor) \(hoursLeftStr):\(minLeftStr):\(secLeftStr)h"
self.statusMenu.update() // not working
}
}
Basically it works, but when I open the NSMenu it won't refresh the NSMenuItem (UI). I found a solution for Objective-C (How to update NSMenu while it's open?) but sadly, I am not able to adapt this using Swift.
You need to read the docu about NSRunLoop when your menu is displayed none of your threads will get control. You need to tell the OS you want to participate.