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
}
}
Related
I'm trying to observe a property (which is declared within an Objective-C) in Swift.
Objective-C protocol:
#protocol DemoViewModel <NSObject>
#property (nonatomic, strong) NSString *bla;
#end
Swift observe:
#objc public dynamic var vm: (NSObject & DemoViewModel) {
didSet {
vm.observe(#keyPath(DemoViewModel.bla)) { _,_ in
//do something
}
}
}
Interestingly I receive an error:
Member ‘observe’ cannot be used on value of protocol type ‘NSObject & DemoViewModel’; use a generic constraint instead
Any idea what's going on?
Thanks
There must be something special under the hood about the observe function, since all the functions and methods can be accessed your way. It's asking you to instead create something like this:
class Foo {
#objc public dynamic var vm: (NSObject & DemoViewModel)?
private var observer: NSKeyValueObservation?
func setVM<T>(_ vm: T) where T: NSObject &: DemoViewModel {
self.vm = vm
observer = vm?.observe(\.bla, options: [.old, .new]) { _, _ in
// do something
}
}
}
When you implement DemoViewModel, remember that you need to mark bla as dynamic, otherwise the observer won't be called.
class Bar: NSObject, DemoViewModel {
#objc dynamic var bla: String = ""
}
I have a custom Objective-C class that contains two CGFloats:
#property (nonatomic, assign) IBInspectable CGFloat minimumConstant NS_REFINED_FOR_SWIFT;
#property (nonatomic, assign) IBInspectable CGFloat maximumConstant NS_REFINED_FOR_SWIFT;
Both are marked as IBInspectable. In the initializer of the class I set both bot NaN as I need to represent a difference between 0 and nothing. To make the class nice to use from Swift I marked both properties as NS_REFINED_FOR_SWIFT and created a Swift extension with the refined implementation:
#IBInspectable public var minimumConstant: CGFloat? {
get {
let constant = __minimumConstant
return constant.isNaN ? nil : constant
}
set {
if let constant = newValue {
__minimumConstant = constant
return
}
__minimumConstant = CGFloat.nan
}
}
#IBInspectable public var maximumConstant: CGFloat? {
get {
let constant = __maximumConstant
return constant.isNaN ? nil : constant
}
set {
if let constant = newValue {
__maximumConstant = constant
return
}
__maximumConstant = CGFloat.nan
}
}
This works great when using my class from code. When using Swift NaN will be mapped to nil and I can use optional unwrapping as usual.
The issue is that as soon as I add NS_REFINED_FOR_SWIFT the Interface Builder will no longer recognize my properties as IBInspectable and does not show them.
Is this a known issue? Is there any workaround for this?
I am looking for a general solution to access:
Obj-C named property getters and named property setters from Swift
Conform to an Objective-C #protocol with readonly properties
Similar to Creating an Objective-C equivalent Getter and Setter in Swift, which is closed, yet does not offer a satisfying answer.
Objective-C to Swift Properties Example:
I have an Objective-C protocol defined with two problematic properties, one with a custom getter isEnabled and another with a private setter exists.
#protocol SomeProtocol <NSObject>
#property (nonatomic, assign, getter = isEnabled) BOOL enabled;
#property (nonatomic, readonly) BOOL exists;
#end
How can I access these Objective-C properties from Swift?
This does not seem to work:
func isEnabled() -> Bool { return self.enabled }
and neither does:
var isEnabled:Bool {
get { }
set { }
}
Straight from the documentation:
Use the #objc(<#name#>) attribute to provide Objective-C names for properties and methods when necessary. For example, you can mark a property called enabled to have a getter named isEnabled in Objective-C like this:
SWIFT
var enabled: Bool {
#objc(isEnabled) get {
/* ... */
}
}
Named Objective-C Getter Property in Swift
var _enabled:Bool = false
var enabled:Bool {
#objc(isEnabled) get {
return self._enabled
}
set(newValue){
_enabled = newValue
}
}
readonly Objective-C Property in Swift
Either
var _exists:Bool = false
private(set) var exists:Bool {
get{
return self._exists
}
set(newValue){
self._exists = newValue
}
}
or
var _exists:Bool = false
var exists:Bool {
get{
return self._exists
}
}
and access self._exists directly since there is no setter.
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;
}
}
I'm fairly new to Objective-C and wondering if it's possible to type objects as their supertype without receiving compiler warnings when assigning them, or if there is a recognised way of achieving the same thing?
I realise that this is what type id is for but I have a base class with synthesized properties and if I try to use id I get a build error "request for member 'x' in something not a structure or union", presumably because dynamic typing is fine for sending messages to an object but not for accessing synthesized properties.
For example in Java I might have:
public abstract class A {
public function doSomething() {
//some func
}
}
public class B extends A {
public function doSomething() {
//override some func
}
}
public class C extends A {
public function doSomething() {
//override some func
}
}
//and in my main class:
A objB = new B();
A objC = new C();
//the purpose of all of this is so I can then do:
A objHolder;
objHolder = objB;
objHolder.doSomething();
objHolder = objC;
objHolder.doSomething();
I currently have the above working in Objective-C but with a compiler warning: "assignment from distinct Objective-C type"
OK, here is the Objective-C interfaces, I can add the implementations if you want. It's a composite pattern:
//AbstractLeafNode
#import <Foundation/Foundation.h>
#interface AbstractLeafNode : NSObject {
NSString* title;
AbstractLeafNode* parent;
}
#property (nonatomic, retain) NSString* title;
#property (nonatomic, retain) AbstractLeafNode* parent;
#end
//Page
#import "AbstractLeafNode.h"
#interface Page : AbstractLeafNode {
//there will be stuff here later!
}
#end
//Menu
#import "AbstractLeafNode.h"
#interface Menu : AbstractLeafNode {
NSMutableArray* aChildren;
}
- (void)addChild:(AbstractLeafNode *)node;
- (void)removeChild:(AbstractLeafNode *)node;
- (AbstractLeafNode *)getChildAtIndex:(NSUInteger)index;
- (AbstractLeafNode *)getLastChild;
- (NSMutableArray *)getTitles;
#end
// I'd then like to do something like (It works with a warning):
AbstractLeafNode* node;
Menu* menu = [[Menu alloc] init];
Page* page = [[Page alloc] init];
node = menu;
[node someMethod];
node = page;
[node someMethod];
// Because of the synthesized properties I can't do this:
id node;
// I can do this, but I suspect that if I wanted synthesized properties on the page or menu it would fail:
node = (AbstractLeafNode*)menu;
node = (AbstractLeadNode*)page;
Sorry, as I was editing the question I realised that I was trying to do it the wrong way round and assign an AbstractLeafNode to a Menu, so the compiler warning completely makes sense. No errors when assigning a Menu to an AbstractLeafNode.
I've been staring at this for too long!