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?
Related
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()
}
}
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
}
}
I have two delegates like this:
protocol MyFirstDelegate {
func change(value: int)
}
protocol MySecondDelegate {
weak var delegate: MyFirstDelegate?
}
Those protocols are implemented in two customized UIViewControllers:
class MyFirstViewController: UIViewController, MyFirstDelegate {
...
}
class MySecondViewController: UIViewController, MySecondDelegate {
...
}
The first UI view controller will push second UI view controller by using a segue.
In my first UI view controller class I have the following codes to prepare segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if let vc = (segue.destinationViewController) as? MySecondViewController {
vc.delegate = self
}
}
The above codes compile without any problem. However, I think that the first UI controller has knowledge about the next view controller class. I prefer to pass delegate like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if var vc = (segue.destinationViewController) as? MySecondDelegate {
vc.delegate = self
}
}
I got compiling error:
Cannot assign to property: "vc" is immutable.
It seems that as to a concrete UI view controller is OK, but not to a protocol. Any explanation and solution to this?
By the way, in Objective-C, this can be done like this:
id<NSObject, MySecondDelegate> vc = segue.destinationViewController;
vc.delegate = self;
I could not find a way in swift to define a variable like
let vc: AnyObject<type>? // like id<type>
UPDATES
As #David_Berry solution, it works if I mark protocol as class. The above codes are actually simplified ones. I am in the middle of converting my ObjC codes to Swift. Therefore, I have to mark protocols as objc:
#objc protocol MyFirstDelegate : class {
func change(value: int)
}
#objc protocol MySecondDelegate {
weak var delegate: MyFirstDelegate?
}
with #objc, I still get the same compiling error.
Well, I finally managed to get your example to compile, you have a LOT of inconsistencies. The key to your actual problem, I think, is requiring that the protocols be class protocols:
protocol XXX : class { }
Says that only types that are classes can be declared to implement XXX.
But, I'm really not sure that's your problem, there were so many other changes required (you spelled MySecondDelegate at least two ways, MySecondViewController at least two ways, etc.
The compiling example is:
// This protocol has to be class so that delegate can be weak
protocol MyFirstDelegate : class {
func change(value: Int)
}
protocol MySecondDelegate {
weak var delegate: MyFirstDelegate? { get set }
}
class MyFirstViewController: UIViewController, MyFirstDelegate {
func change(value: Int) {
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if var vc = (segue.destinationViewController) as? MySecondDelegate {
vc.delegate = self
}
}
}
class MySecondViewController: UIViewController, MySecondDelegate {
weak var delegate : MyFirstDelegate?
}
As for your question, there is no swift synonym to the objective-C syntax id<Protocol> or Class<Protocol> There are multiple duplicates to that issue that offer alternatives.
Could somebody explain to me how I can set acceptsFirstResponder on a NSViewController in Swift and xcode 6?
The program doesn't allow any overriding of the function and says it doesn't exist in the superclass but I can also not set it as a variable like:
self.acceptsFirstResponder = true
Anybody know?
I had a similar issue but with a NSView Class. I was able to make this work with the following. I had to put it in the class outside any functions.
import Cocoa
class MyView: NSView, NSDraggingDestination {
//MARK: Variables
override var acceptsFirstResponder: Bool { return true }
override var opaque: Bool { return true }
var bgColor : NSColor = .yellowColor()
var lastString : String = ""
var attributes : NSMutableDictionary = [:]
var highlightNeeded = false
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;
}
}