I'm working on iOS 8 custom keyboard extension right now, and there are some issues in using UITextInputDelegate Methods.
Does this right: selectionWillChange: and selectionDidChange: methods should be called when user long-presses typing area? And textWillChange: and textDidChange: methods should be called whenever the text is literally changing?
Actually, what I observed is that, when I changed selection in text input area, textWillChange: and textDidChange: are called, and I cannot get a clue that the other two methods are called in what condition. If anyone knows about the usage of these delegate methods, please let me know.
It's not working for me either... what I am currently doing is just using textWillChange and textDidChange which does get called, as you mentioned, when you change your selection... (they get called BEFORE and AFTER) And then comparing the: self.textDocumentProxy.documentContextBeforeInputself.textDocumentProxy.documentContextAfterInput From BEFORE (textWillChange) to the AFTER (textDidChange) to see if selection range or length changed at all.
Something like this (set the 4 NSStrings below in your .h file of course... haven't tested this exact snippet because I wrote it from scratch just now on SO.com but I'm sure the principle works if I made any errors)
- (void)textWillChange:(id<UITextInput>)textInput {
beforeStringOfTextBehindCursor = self.textDocumentProxy.documentContextBeforeInput;
beforeStringOfTextAfterCursor = self.textDocumentProxy.documentContextAfterInput;
}
- (void)textDidChange:(id<UITextInput>)textInput {
afterStringOfTextBehindCursor = self.textDocumentProxy.documentContextBeforeInput;
afterStringOfTextAfterCursor = self.textDocumentProxy.documentContextAfterInput;
BOOL didSelectionChange = NO;
if (![beforeStringOfTextBehindCursor isEqualToString:afterStringOfTextBehindCursor]) {
didSelectionChange = YES;
}
if (![beforeStringOfTextAfterCursor isEqualToString:afterStringOfTextAfterCursor]) {
didSelectionChange = YES;
}
if (didSelectionChange) {
NSLog(#"Selection Changed!");
}
}
I had the same problem with the functions specified in the UITextInput protocol not being called. The reason as far as I can discern is due to the fact that the inputDelegate is set at runtime. According to the ios docs:
The UIKit provides a private text input delegate, which it assigns at runtime to the inputDelegate property of the object whose class adopts the UITextInput protocol. link
The fix which works in my case is to reset the inputDelegate in the function:
textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
by the following line:
[myUITextField setInputDelegate:self];
where self implements the UITextInputDelegate protocol.
Related
I was looking at adding an option for sorting available fonts by user-defined font collections (I wish Pages and Keynote did this!), but it looks like the old ways of accessing these collections are being deprecated in 10.11:
https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSFontManager_Class/index.html#//apple_ref/occ/clm/NSFontManager/
Is there a new way of accessing and using those font collections?
I've recently been working with font collections, so I may have some info for you.
The NSFontCollection API is kind of strange. It offers access to "Named Font Collections," but the names associated with said collections aren't attached to them. If that makes no sense, it's because it doesn't. However, let me try to break it down:
To add a system-wide font collection:
// Create a font descriptor (or descriptors) with whatever attributes you want
let descriptor = NSFontDescriptor(fontAttributes: nil)
// Create a font collection with the above descriptor(s)
let collection = NSFontCollection(descriptors: [descriptor])
// In Objective-C, the `+ showFontCollection:withName:visibility:error:`
// method returns `YES` if the collection was shown or `NO` if an error occurred.
// The error is passed via pointer supplied to the `error:` parameter.
//
// In Swift, the method returns `()` (aka nil literal), and instead of passing
// the error in a pointer, it `throws`, so you have to wrap the call in a `do`
// statement and catch the error (or suppress error propagation via `try!`):
do {
try NSFontCollection.showFontCollection(collection: NSFontCollection,
withName: "Visible to All Users", visibility: .Computer)
}
catch {
print("There was an error showing font collection. Info: \(error)")
}
To add a user-visible font collection:
Repeat above steps, substituting .Computer with .User:
do {
try NSFontCollection.showFontCollection(collection: NSFontCollection,
withName: "Visible to the Current User", visibility: .User)
}
catch {
print("There was an error showing font collection. Info: \(error)")
}
To add a non-persistent font collection:
Repeat above steps, substituting .Computer with .Process:
do {
try NSFontCollection.showFontCollection(collection: NSFontCollection,
withName: "Visible for Current Process", visibility: .Process)
}
catch {
print("There was an error showing font collection. Info: \(error)")
}
Next steps…
Once you have a collection, you can change it all you want using the NSMutableFontCollection class. Continuing with the example above, you would do something like this:
let mutableCollection = collection.mutableCopy() as! NSMutableFontCollection
let boldTrait = [NSFontWeightTrait: NSFontWeightBold]
let boldAttributes = [NSFontTraitsAttribute: boldTrait]
let boldDescriptor = NSFontDescriptor(fontAttributes: newAttributes)
mutableCollection.addQueryForDescriptors([boldDescriptor])
At this point, the API gets weird again. We've added descriptors to our "named collection," but nothing in the UI is going to show up until you "show" the font collection again. In other words, after making any changes, you have to call showFontCollection(_:withName:visibility:) again.
Likewise, if you want to remove/delete a collection, you have to call hideFontCollectionWithName(_:visibility:). Despite its innocuous name, this method completely removes a persistent collection from disk, so be careful.
Next next steps…
In subsequent launches of your app, you can retrieve any persistent collection using the NSFontCollection(name:visibility:) method, like so:
// Retrieve collection created at an earlier time
let collectionOnDisk = NSFontCollection(name: "Visible to All Users", visibility: .Computer)
I think I've covered most of it, but if I've missed something, or if you have questions, just let me know. Good luck!
There are classes NSFontCollection and NSFontDescriptor.
Look into the NSFontManager header file of Xcode 7 (via ⇧⌘O) to get more information about the deprecated methods and their replacement.
Since I am fairly new to Swift programming on OSX, this question may contain several points that needs clarification.
I have a method which iterates over all subviews of a given NSView instance. For this, I get the array of subviews which is of type [AnyObject] and process one element at a time.
At some point I would like to access the identifier property of each instance. This property is implemented from a protocol in NSView named NSUserInterfaceItemIdentification, which type is given in the documentation as (optional) String?. In order to get that identifier I would have written
var view : NSView = subview as NSView;
var viewIdent : String = view.identifier!;
The second line is marked by the compiler with an error stating that identifier is not of an optional type, but instead of type String, and hence the post-fix operator ! cannot be applied.
Removing this operator compiles fine, but leads to a runtime error EXC_BAD_ACCESS (code=1, address=0x0) because identifier seems to be nil for some NSButton instance.
I cannot even test for this property, because the compiler gives me a String is not convertible to UInt8 while I try
if (view.identifier != nil) {viewIdent = view.identifier;}
My questions are
Is the documentation wrong? I.g. the property identifier is not optional?
How can I ship around this problem and get code that runs robust?
If the documentation states that view.identifier is an Optional, it means it can be nil. So it's not a surprise that for some button instances it is indeed nil for you.
Force unwrapping this element that can be nil will lead your app to crash, you can use safe unwrapping instead:
if let viewIdent = view.identifier {
// do something with viewIdent
} else {
// view.identifier was nil
}
You can easily check the type of an element in Xcode: click on the element while holding the ALT key. It will reveal a popup with informations, including the type. You can verify there that your element is an Optional or not.
Tip: you can safe unwrap several items on one line, it's rather convenient:
if let view = subview as? NSView, viewIdent = view.identifier {
// you can do something here with `viewIdent` because `view` and `view.identifier` were both not nil
} else {
// `view` or `view.identifier` was nil, handle the error here
}
EDIT:
You have to remove this line of yours before using my example:
var viewIdent : String = view.identifier!
Because if you keep this line before my examples, it won't work because you transform what was an Optional in a non-Optional by adding this exclamation mark.
Also it forces casting to a String, but maybe your identifier is an Int instead, so you shouldn't use this kind of declaration but prefer if let ... to safe unwrap and cast the value.
EDIT2:
You say my example doesn't compile... I test every answer I make on SO. I tested this one in a Playground before answering, here's a screenshot:
Also, after checking it, I confirm that the identifier is an Optional String, that's the type given by Xcode when using ALT+CLICK on the property. The documentation is right.
So if it's different for you, it means you have a different problem unrelated to this one; but my answer for this precise question remains the same.
I've got some code that runs a fairly complex algorithm. I want to put together a fairly simple UI that will allow me to monitor the values of the various variables in the algorithm in some graphical ways -- think of it like a dashboard of sorts.
So, for simplicity's sake, let's say I have an algorithm like what follows. It searches a vector of values for the two values that most closely sum to a target value:
import Foundation
class algorithm {
var numbers = [Double]()
let numberOfRandoms = 1000
dynamic var a: String
dynamic var b: String
init () {
// Load initial vector with some random numbers between 0 and 1
for _ in 1...numberOfRandoms {
numbers.append(Double(arc4random()) / Double(UINT32_MAX))
}
a = " "
b = " "
}
func findTheTwoNumbersThatAddUpTheClosestToTarget(target: Double) {
//Initializing this to a very large value
var currentBestSum = 1000.0
//Begin brute force search for the optimal solution
for i in 0...numbers.count-2 {
for j in i+1...numbers.count-1 {
//Check to see if the current candidate exceeds the best solution
if abs(numbers[i] + numbers[j] - target) < currentBestSum {
//If it does, store the new champion
a = String(i)
b = String(j)
//And reset the current top score to match
currentBestSum = abs(numbers[i] + numbers[j]-target)
}
}
}
}
}
Now, this is just a simple (and silly) example, but it suits these purposes. I basically want to create a simple UI that displays the important values in the process as it runs (dynamically).
In this example, let's say that I just want to display two labels that contain the index values of the two leaders as the algorithm executes.
I created the labels in the storyboard.
Then, I created IBOutlets in the ViewController (Actually, storyboards did it for me when I Ctrl-dragged):
class ViewController: NSViewController {
#IBOutlet weak var a: NSTextField!
#IBOutlet weak var b: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Then, I ctrl-dragged the labels to the a and b in the algorithm class to create the bindings.
Finally, I create an class variable in the view controller and instantiate it in the viewDidLoad method. This doesn't seem like the right thing to do -- maybe it is. Seems like you would want to keep separate your interface and data...
The labels do, in fact, show up -- but they never show any values of a and b. They just show the default text.
Not sure what I'm doing wrong.
Help!?
P.S., in response to Anthony Kong, I do recognize that I could manually synchronize all the view elements in the code, but I thought the whole point of using bindings was to avoid having to do this manual synchronization. I just can't figure out how to set it up.
Without commenting on your specific code I think I have experienced (and solved) the problem you describe. I was able to write an app that had two targets, one NIB-based and one Storyboard-based. As much as I was able I duplicated the code in each and shared the common data instance that I was trying to display in a TableView. The NIB-based app worked using the stock Cocoa Bindings that I set in IB. But the Storyboard-based app did not, the array controller did not see the data.
My solution was simply to add the binding for contentArray programmatically in viewDidLoad. The one line that fixed it for me is:
ac.bind("contentArray", toObject: cd, withKeyPath: "people", options: nil)
ac is the IBOutlet for the ArrayController in the Storyboard. cd is the class instance that contains the people array.
This is using XCode 6.2 (6C107a) which is Beta 3 I think.
This was the only binding that I had to set myself, the TableView to ArrayController (arrangedObjects) and TableViewCell to TableView (objectValue) didn't need any tweaking.
There are several problems with your code
1) In your code,
func applicationDidFinishLaunching(aNotification: NSNotification?) {
// Insert code here to initialize your application
var test = algorithm()
test.findTheTwoNumbersThatAddUpTheClosestToTarget(0.5)
}
The variable test goes out of scope when the function exits. Based on the wording of your question, you are expecting it to be a long running process. So this will not do what you want.
You should
a) Add test as class variable to the ViewController class
b) instantiate the variable in viewDidLoad method.
2) In your algorithm it does not actually provide any feedback to the labels. Maybe you think because the class has the ivar a and b so they are hooked to the IBOutlet by the same names. But of course it is not the case. And you do not need the keyword dynamic too.
What you should do is:
a) provide a method in the View Controller class to update the labels. It will serve as a callback function to be used by algorithm class to feedback the calculation result.
It may look like this:
func update_value_callback(vala: String, valb: String) {
a.text = vala; // updating the label here
b.text = valb;
}
b) make the algorithm class calls the callback function e.g.
func findTheTwoNumbersThatAddUpTheClosestToTarget(target: Double, viewController: ViewController) {
// do your stuff
...
// execute the callback
viewController.update_value_callback(a, b)
}
I have a custom view with two text subviews, arranged, not that it matters, as per this amazing ASCII art:
/--------\
| lblOne |
| lblTwo |
\--------/
On my controller, I have a property of type Thingy:
class AwesomeController: NSViewController {
var thingy: Thingy! = nil
}
A Thingy has two properties of interest:
class Thingy: NSObject {
var one: String
var two: String
}
I would like to set up a binding between lblOne's string value and thingy.one, and lblTwo's string value and thingy.two, going through a custom view class if necessary.
When thingy is changed, obviously the two text fields should also change. (In other words, it should behave normally for a cocoa binding.)
I think it's probably a combination of learning Swift and my unfamiliarity with storyboards on OS X (last time I did cocoa development, it was still xibs), but I can't work out how to link the damn thing up.
Getting bindings to work in Swift requires two additional steps:
All the vars must be marked with dynamic, eg:
dynamic var one: String
You have to recompile the project (with cmd+B, not just in the background) in order for the var to appear as an option in IB
Before Swift, in Objective-C I would swizzle or hook methods in a class using <objc/runtime.h>.
If anyone has any info on the topic of modifying Swift's runtime and hooking functions like CydiaSubstrate and other libraries that helped in this area, please inform me.
I've succeed with method swizzling in Swift. This example shows how to hook description method on NSDictionary
My implementation:
extension NSDictionary {
func myDescription() -> String!{
println("Description hooked")
return "Hooooked " + myDescription();
}
}
Swizzling code:
func swizzleEmAll() {
var dict:NSDictionary = ["SuperSecret": kSecValueRef]
var method: Method = class_getInstanceMethod(object_getClass(dict), Selector.convertFromStringLiteral("description"))
println(dict.description) // Check original description
var swizzledMethod: Method = class_getInstanceMethod(object_getClass(dict), Selector.convertFromStringLiteral("myDescription"))
method_exchangeImplementations(method, swizzledMethod)
println(dict.description) //Check that swizzling works
}
Edited:
This code will work for any custom Swift class that inherits from NSObject (but will not work for classes that don't.) More examples - https://github.com/mbazaliy/MBSwizzler
You would likely be able to swizzle swift-generated classes that inherit from Objective-C classes with no problem, since they appear to use dynamic method dispatch all the time. You may be able to swizzle methods of swift-defined classes that exist in the Objective-C runtime by virtue of being passed across the bridge, but the Objective-C side methods are likely to just be proxies back across the bridge to the swift-side runtime, so it's not clear that it'd be particularly helpful to swizzle them.
"Pure" swift method calls do not appear to be dispatched dynamically via anything like objc_msgSend and it appears (from brief experimentation) that the type safety of swift is implemented at compile time, and that much of the actual type information is absent (i.e. gone) at runtime for non-class types (both of which likely contribute to the purported speed advantages of swift.)
For these reasons, I expect that meaningfully swizzling swift-only methods will be significantly harder than swizzling Objective-C methods, and will probably look a lot more like mach_override than Objective-C method swizzling.
I'm answering this question more than one year later because none of the other answers provide the definitive set of requirements for method swizzling for every kind of class.
What is described by other, while it will work flawlessly for extensions to foundation/uikit classes (like NSDictionary), will simply never work for your own Swift classes.
As described here, there is an additional requirement for method swizzling other than extending NSObject in your custom class.
The swift method you want to swizzle must be marked dynamic.
If you don't mark it, the runtime will simply continue to call the original method instead of the swizzled one, even if the method pointers appear to have been swapped correctly.
Update:
I've expanded this answer in a blog post.
I had a Xcode 7 iOS project written in Swift 2, using Cocoapods. In a specific Cocoapod, with Objective-C source, I wanted to override a short method, without forking the pod. Writing a Swift extension wouldn't work in my case.
For using method swizzling, I created a new Objective-C class in my main bundle with the method I wanted to replace/inject into the cocoapod. (Also added the bridging header)
Using mbazaliy 's solution on stackflow, I put my code similar to this into the didFinishLaunchingWithOptions in my Appdelegate:
let mySelector: Selector = "nameOfMethodToReplace"
let method: Method = class_getInstanceMethod(SomeClassInAPod.self, mySelector)
let swizzledMethod: Method = class_getInstanceMethod(SomeOtherClass.self, mySelector)
method_exchangeImplementations(method, swizzledMethod)
This worked perfectly. The difference between #mbazaliy 's code is that I didn't need to create an instance of the SomeClassInAPod class first, which in my case would have been impossible.
Note: I put the code in the Appdelegate because every other time the code runs, it exchanges the method for the original - it should only run one time.
I also needed to copy some assets that were referenced in the Pod's bundle to the main bundle.
I wouldn't do it that way, I think closures provide the answers (as they give you a chance to intercept, evaluate, and forward the invocation of the function, additionally it will be easy to extend when and if we have reflection.
http://www.swift-studies.com/blog/2014/7/13/method-swizzling-in-swift
I would like to extend the great answer provided by mbazaliy.
Another way of doing swizzling in Swift is by providing an implementation using an Objective-C block.
e.g. to replace descriptionmethod on class NSString we can write:
let originalMethod = class_getInstanceMethod(NSString.self, "description")
let impBlock : #objc_block () -> NSString =
{ () in return "Bit of a hack job!" }
let newMethodImp = imp_implementationWithBlock(unsafeBitCast(impBlock, AnyObject.self))
method_setImplementation(originalMethod, newMethodImp)
This works as of Swift 1.1.
A safe, easy, powerful and efficient hook framework for iOS (Support Swift and Objective-C). https://github.com/623637646/SwiftHook
For example, this is your class
class MyObject {
#objc dynamic func noArgsNoReturnFunc() {
}
#objc dynamic func sumFunc(a: Int, b: Int) -> Int {
return a + b
}
#objc dynamic class func classMethodNoArgsNoReturnFunc() {
}
}
The key words of methods #objc and dynamic are necessary
The class doesn't have to inherit from NSObject. If the class is written by Objective-C, Just hook it without any more effort
Perform the hook closure before executing specified instance's method.
let object = MyObject()
let token = try? hookBefore(object: object, selector: #selector(MyObject.noArgsNoReturnFunc)) {
// run your code
print("hooked!")
}
object.noArgsNoReturnFunc()
token?.cancelHook() // cancel the hook
Perform the hook closure after executing specified instance's method. And get the parameters.
let object = MyObject()
let token = try? hookAfter(object: object, selector: #selector(MyObject.sumFunc(a:b:)), closure: { a, b in
// get the arguments of the function
print("arg1 is \(a)") // arg1 is 3
print("arg2 is \(b)") // arg2 is 4
} as #convention(block) (Int, Int) -> Void)
_ = object.sumFunc(a: 3, b: 4)
token?.cancelHook() // cancel the hook
The key word #convention(block) is necessary
For hook at before and after. The closure's args have to be empty or the same as method. The return type has to be void
Totally override the mehtod for specified instance. You can call original with the same parameters or different parameters. Don't even call the original method if you want.
let object = MyObject()
let token = try? hookInstead(object: object, selector: #selector(MyObject.sumFunc(a:b:)), closure: { original, a, b in
// get the arguments of the function
print("arg1 is \(a)") // arg1 is 3
print("arg2 is \(b)") // arg2 is 4
// run original function
let result = original(a, b) // Or change the parameters: let result = original(-1, -2)
print("original result is \(result)") // result = 7
return 9
} as #convention(block) ((Int, Int) -> Int, Int, Int) -> Int)
let result = object.sumFunc(a: 3, b: 4) // result
print("hooked result is \(result)") // result = 9
token?.cancelHook() // cancel the hook
For hook with instead. The closure's first argument has to be a closure which has the same types with the method. The rest args and return type have to be the same as the method.
Perform the hook closure before executing the method of all instances of the class.
let token = try? hookBefore(targetClass: MyObject.self, selector: #selector(MyObject.noArgsNoReturnFunc)) {
// run your code
print("hooked!")
}
MyObject().noArgsNoReturnFunc()
token?.cancelHook() // cancel the hook
Perform the hook closure before executing the class method.
let token = try? hookClassMethodBefore(targetClass: MyObject.self, selector: #selector(MyObject.classMethodNoArgsNoReturnFunc)) {
// run your code
print("hooked!")
}
MyObject.classMethodNoArgsNoReturnFunc()
token?.cancelHook() // cancel the hook
After spending some time on it... Wake up this morning.... beta 6 is out and
Problem Fixed in beta6!
From release notes
"Dynamic dispatch can now call overrides of methods and properties introduced in class extensions, fixing a regression introduced in Xcode 6 beta 5. (17985819)!"