monomac / Xamarin.Mac - derived NSTextField using NSTextViewDelegate - mono

In objective-C, I can make my subclass of NSTextField conform to the the NSTextViewDelegate protocol - like so:
#interface PasswordField : NSTextField <NSTextViewDelegate>
How can I translate this idiom to C# / monomac?
I have tried subclassing NSTextViewDelegate:
private class TextViewDelegate : NSTextViewDelegate
{}
And assigning that to the delegate property of my NSTextField subclass:
public class PasswordField : NSTextField
{
public PasswordField(NSCoder coder) : base(coder)
{
this.Delegate = new TextViewDelegate();
}
}
However, obviously this does not work since the Delegate property of NSTextField is (correctly) typed as NSTextFieldDelegate.
Error CS0029: Cannot implicitly convert type `PasswordFieldControl.PasswordField.TextViewDelegate' to `MonoMac.AppKit.NSTextFieldDelegate' (CS0029)
So how to make this work as it does in objective-C?

There are two ways to do this:
If you are fine with keeping the delegate separate, you can do this:
class TextViewDelegate : NSTextViewDelegate
{
public override void TextDidChange (NSNotification notification)
{
}
}
public class PasswordField : NSTextField
{
public PasswordField(NSCoder coder) : base(coder)
{
this.WeakDelegate = new TextViewDelegate();
}
}
or, if you want to use the same PasswordField object:
public class PasswordField : NSTextField
{
[Export("textDidChange:")]
public void TextDidChange (NSNotification notification)
{
}
public PasswordField(NSCoder coder) : base(coder)
{
this.WeakDelegate = this;
}
}

Related

resolveInstanceMethod calling from Swift code

How can I call + (BOOL)resolveInstanceMethod:(SEL)aSEL while changing dynamic variable variable name?
E.g.
I've got an ObjC class:
#interface SomeClass: NSObject
///
#end
#implementation SomeClass: NSObject
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
//// code
}
#end
Swift
#objcMembers
public final class SomeSwiftClass: SomeClass {
public dynamic var value: NSNumber?
public dynamic func mmFunc() {
print("mmFunc")
}
}
code:
let anInstance = SomeSwiftClass()
anInstance.value = NSNumber(10) /// how can I call resolveInstanceMethod? Doesn't work
anInstance.mmFunc() //doesn't work too.
If I implement the same code in ObjC it works.
Any ideas?
All the following works w/o changes in your classes (tested with Xcode 11.2 / iOS 13.2).
func test() {
let anInstance = SomeSwiftClass()
let hasMethod = SomeSwiftClass.resolveInstanceMethod(#selector(SomeSwiftClass.mmFunc))
print("Result: \(hasMethod)")
anInstance.value = NSNumber(10)
anInstance.mmFunc()
}
if not at your side, see if objc-swift bridging is correctly configured and all needed includes are added.
Update: compiler was confused by ambiguous name of value, changing it to other more specific name works as below
#interface SomeClass: NSObject
#property (nonatomic) BOOL isValid;
#end
#objc
public class SomeSwiftClass: SomeClass {
#objc public var specificValue: NSNumber = NSNumber(5)
#objc public func mmFunc() {
print("mmFunc")
}
}
#objc
final class Runner: NSObject {
#objc public static func test() {
let anInstance = SomeSwiftClass()
let hasProperty = SomeSwiftClass.resolveInstanceMethod(#selector(getter: SomeSwiftClass.isValid))
print("Has property: \(hasProperty)")
let swiftProperty = SomeSwiftClass.resolveInstanceMethod(#selector(getter: SomeSwiftClass.specificValue))
print("Swift property: \(swiftProperty)")
let hasMethod = SomeSwiftClass.resolveInstanceMethod(#selector(SomeSwiftClass.mmFunc))
print("Has method: \(hasMethod)")
anInstance.specificValue = NSNumber(10)
anInstance.mmFunc()
}
}

Bridging Swift Initialisers to Objective C

I'm attempting to slowly migrate an Objective C app over to Swift and have started to create new classes -
public class MapsAPI : NSObject {
let delegate: MapsAPIResponseDelegate
public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}
Now in my Objective C .m file I've declared #import MyTarget-Swift.h and in my .h I've added #class MapsAPI which all seems fine however I'm not sure what the Objective C initialisation code should look like. I've tried -
MapsAPI *api = [[MapsAPI alloc] initWithManagerWithDelegate: self];
But that errors with -
No visible #interface for 'MapsAPI' declares the selector
'initWithManagerWithDelegate:'
I've tried looking at the definition of my MyTarget-Swift.h but all that shows is -
SWIFT_CLASS("_TtC4What7MapsAPI")
#interface MapsAPI : NSObject
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
#end
Is there something I'm doing wrong here?
You may choose to add #objcMembers to your class declaration:
public class #objcMembers MapsAPI : NSObject {
let delegate: MapsAPIResponseDelegate
public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}
Alternatively (or additionally... who am I to judge) you can mark your initializer as being exposed to Objective-C
public class MapsAPI : NSObject {
let delegate: MapsAPIResponseDelegate
#objc public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}
And if you want to, you can also explicitly define the Objective-C selector used:
public class MapsAPI : NSObject {
let delegate: MapsAPIResponseDelegate
#objc(initManagerWithDelegate:)
public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}

How am I not following this protocol?

Is there something I don't understand?
Protocol:
public protocol SLKTypingIndicatorProtocol : NSObjectProtocol {
/**
Returns YES if the indicator is visible.
SLKTextViewController depends on this property internally, by observing its value changes to update the typing indicator view's constraints automatically.
You can simply #synthesize this property to make it KVO compliant, or override its setter method and wrap its implementation with -willChangeValueForKey: and -didChangeValueForKey: methods, for more complex KVO compliance.
*/
public var visible: Bool { get set }
/**
Dismisses the indicator view.
*/
optional public func dismissIndicator()
}
My code:
public class TypingListView: UIView, SLKTypingIndicatorProtocol {
var _visible: Bool = false
public var visible: Bool {
get {
return self._visible
}
set (val) {
self._visible = val
}
}
public func isVisible() -> Bool {
return self.visible
}
public func dismissIndicator() {
self.visible = false
}
// Other code...
}
The error I keep getting: "Type 'TypingListView' does not conform to protocol 'SLKTypingIndicatorProtocol'"
When I expand the error it states: "Protocol requires property 'visible' with type 'Bool'". It also says "Objective-C method 'visible' provided by getter for 'visible' does not match the requirement's selector ('isVisible')"
I found how the protocol actually reads in Objective-C as well:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** Generic protocol needed when customizing your own typing indicator view. */
#protocol SLKTypingIndicatorProtocol <NSObject>
#required
/**
Returns YES if the indicator is visible.
SLKTextViewController depends on this property internally, by observing its value changes to update the typing indicator view's constraints automatically.
You can simply #synthesize this property to make it KVO compliant, or override its setter method and wrap its implementation with -willChangeValueForKey: and -didChangeValueForKey: methods, for more complex KVO compliance.
*/
#property (nonatomic, getter = isVisible) BOOL visible;
#optional
/**
Dismisses the indicator view.
*/
- (void)dismissIndicator;
#end
NS_ASSUME_NONNULL_END
Tips, try this style:
public var visible: Bool {
#objc(isVisible) get {
return self._visible
}
set (val) {
self._visible = val
}
}

Can I define methods for properties on a Swift class like in Objective-C?

Is it possible to define a method for a property on a Swift class
For example, I have a class named Test in Objective-C, and this class has one property called testProperty of NSString type
Test.h
#interface Test : NSObject
#property (nonatomic, readonly) NSString *testProperty;
#end
I define a getter method in the implementation for this property:
Test.m
#import "Test.h"
#implementation Test
-(NSString*)testProperty
{
return #"testing property like method";
}
#end
How can I do the same in Swift? If make the class in Swift,
import UIKit
class Test: NSObject {
var testProperty : String?
func testProperty() -> String {
return "testing property like method"
}
}
then I receive an error:
"Invalid redeclaration of testProperty()"
These are called Computed properties
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
Example
var testProperty: String {
return "testing property like method"
}
Subclass
class ClassA: NSObject {
var testProperty: String {
return "This is Class A"
}
}
class ClassB: ClassA {
override var testProperty: String {
return "This is Class B"
}
}
let test = ClassB()
print(test.testPropery) // prints "This is Class B"

Unwrap UIWindow twice from AppDelegate in Swift

To Get the rootViewController in Objective-c you can use the below line of code
[[[UIApplication sharedApplication] delegate] window].rootViewController
I tried to do the same in swift
UIApplication.sharedApplication().delegate?.window?.rootViewController
But I got this error
'UIWindow?' does not have a member named 'rootViewController'
And the suggestion says that you have to unwrap UIWindow twice UIWindow?? to be
UIApplication.sharedApplication().delegate?.window??.rootViewController
My Question is: Why do I need to unwrap the window twice ?
.
.
I have looked in the API and found that
protocol UIApplicationDelegate : NSObjectProtocol {
optional var window: UIWindow? { get set }
}
The window has one optional
class UIWindow : UIView {
var rootViewController: UIViewController?
}
Also the rootViewController has one optional
.
.
I thought may be because UIWindow in UIApplicationDelegate protocol has optional and UIWindow? so I tried the below in Playground
#objc protocol MyApplicationDelegate {
optional var myWindow: MyWindow? { get set }
}
class MyWindow : NSObject {
var rootViewController: Int?
init(number: Int) {
rootViewController = number
}
}
class MyAppDelegate: MyApplicationDelegate {
var myWindow: MyWindow?
init() {
myWindow = MyWindow(number: 5)
}
}
let myDelegate = MyAppDelegate()
println(myDelegate.myWindow?.rootViewController)
However I can get myWindow with one optional and can successfully log '5'
What am I missing here?
Well; I found the problem with my example
In my example I'm creating object from MyAppDelegate directly which will sure have myWindow property as I'm defining it
Changing the example to be the following
#objc protocol MyApplicationDelegate {
optional var myWindow: MyWindow? { get set }
}
class MyWindow : NSObject {
var rootViewController: Int?
init(number: Int) {
rootViewController = number
}
}
class MyAppDelegate: MyApplicationDelegate {
var myWindow: MyWindow?
init() {
myWindow = MyWindow(number: 5)
}
}
class MyApplication: NSObject {
var myDelegate: MyApplicationDelegate
override init() {
myDelegate = MyAppDelegate()
}
}
let myApplication = MyApplication()
println(myApplication.myDelegate.myWindow??.rootViewController)
I needed to add another class MyApplication that has a property conform toMyApplicationDelegate
So I was able to use myWindow?? first unwrapping for the optional part in the protocol and second one is for the optional declaration in the variable UIWindow?