I'm now learning a function which is written in Objective-C. But I don't know Objective-C language. When I'm converting the code to Swift, I got stuck at the delegate functions.
The code in .h file is:
#import <UIKit/UIKit.h>
#protocol SCPopViewDelegate <NSObject>
#optional
- (void)viewHeight:(CGFloat)height;
- (void)itemPressedWithIndex:(NSInteger)index;
#end
#interface SCPopView : UIView
#property (nonatomic, weak) id <SCPopViewDelegate>delegate;
#property (nonatomic, strong) NSArray *itemNames;
#end
and I'm trying to convert the code as:
protocol popViewDelegate: class {
func itemPressedWithIndex(index: Int)
func viewHeight(height: CGFloat)
}
but for the three last sentences, i don’t know how to deal with them, especially the one with id and delegate.
Can I get any help, please? I will do more effort to learn Swift. Thank you very much!
Swift 4.2
Converted Code for Swift 4.2
protocol SCPopViewDelegate: class {
func viewHeight(_ height: CGFloat)
func itemPressed(with index: Int)
}
extension SCPopViewDelegate {
func viewHeight(_ height: CGFloat) {}
func itemPressed(with index: Int) {}
}
class SCPopView: UIView {
weak var delegate: SCPopViewDelegate?
var itemNames: [Any] = []
}
This code is based on code converted by an online code conversion tool, the link to the original generated code.
The definition of the protocol ends with the first #end statement.
The #interface after that is the definition of an Objective-C class, SCPopView.
You don't need to care about the latter part if you're just trying to define the protocol in Swift.
FYI:
#interface someClass: NSObject
is equivalent to
class someClass: NSObject
In Swift.
import UIKit
#objc protocol SCPopViewDelegate{
optional func viewHeight(height: Float)
optional func itemPressedWithIndex(index : Int)
}
class DGViewController: UIViewController {
var delegate:SCPopViewDelegate! = nil
override func viewDidLoad() {
super.viewDidLoad()
delegate?.viewHeight?(Float(self.view.frame.size.height))
}
And in the class that implements the delegate
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
var vc = DGViewController()
vc.delegate = self
vc.view.frame = self.view.frame;
vc.view.backgroundColor = (UIColor.redColor())
self.presentViewController(vc, animated: true) { () -> Void in}
}
func viewHeight(height: Float) {
println(height)
}
Related
I have two files
Question.m
Question.h
These two are written by Objective-C
MainView.swift
This is written by Swift
Question Class has the delegate
#interface Question : NSObject{
id delegate;// put MainViewController here
- (void)trythisfunction{
[delegate test] // compiler doesn't find this method.
}
}
and I make class instance and put MainViewController as delegate of Question in MainViewController.swift
class MainViewController: UIViewController {
override func viewDidLoad(){
q = Question()
q.delegate = self // put self in delegate
}
func test(){
NSLog("test is OK")
}
}
However Compiler found error [delegate test]
Question.m:169:19: No known instance method for selector 'test:'
How can I solve this??
You need to make few changes.
Below class declaration doesn't compile because you can't declare variables inside interface.
#interface Question : NSObject{
id delegate;
- (void)trythisfunction {
[delegate test]
}
}
I have fixed above and the class now looks like this,
# Question.h file
#import <Foundation/Foundation.h>
#interface Question : NSObject
#property (nonatomic, strong) id delegate;
#end
Below is the implementation of the class
# Question.m file
#import "Question.h"
#implementation Question
#synthesize delegate;
- (void)trythisfunction{
[delegate test];
}
#end
As we are integrating this swift and so we will need a Bridging Header whose content look like.
#import "Test.h"
Finally in your swift class now you can import this class
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let q = Test()
q.delegate = self
}
func test(){
NSLog("test is OK")
}
}
And above code works like a charm.
We are trying to reference Swift methods inside an Objective-C implementation.
Swift class:
import Foundation
#objc class MySwiftClass: NSObject {
override init() {
super.init()
}
func sayHello() -> Void {
print("hello");
}
func addX(x:Int, andY y:Int) -> Int {
return x+y
}
}
Objective-C implementation (Objective-c.m):
#import "ProductModuleName-Swift.h"
MySwiftClass* getData = [[MySwiftClass alloc]init];
[getData sayHello] //works
[getData addX:5 addY:5] //No visible #interface for 'MySwiftClass' declares selector 'addX:addY'
The last line of code gives the following error:
No visible #interface for 'MySwiftClass' declares selector 'addX:addY'
If you command-click on "ProductModuleName-Swift.h" in the Xcode
source file editor then you can see how the Swift methods are mapped to Objective-C.
In your case that would be
#interface MySwiftClass : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
- (void)sayHello;
- (NSInteger)addXWithX:(NSInteger)x andY:(NSInteger)y;
#end
which is called as
MySwiftClass* getData = [[MySwiftClass alloc]init];
[getData sayHello];
NSInteger result = [getData addXWithX:5 andY:5];
A better Swift 3 method name might be
func add(x: Int, y:Int) -> Int
because x is already the argument (external) name of the first
parameter. You can also add an #objc() attribute to the Swift definition
to control the Objective-C name. For example, with
#objc(addX:andY:)
func add(x: Int, y: Int) -> Int {
return x+y
}
it would be called from Objective-C as
NSInteger result = [getData addX:5 andY:5];
As #ekscrypto pointed out, in Swift 4 and later you need to annotate individual functions with #objc. Prior to that, a single, class-level #objc was enough.
Of course in Objective-C class you must add import of NAME_PROJECT-swift.h.
If your project name is Sample then you must add:
#import Sample-swift.h
And then:
Swift 4 or Less
#objc class MySwiftClass: NSObject {
func sayHello(){
//function
}
func addX(){
//function
}
}
Swift 4 or Greater
#objc class MySwiftClass: NSObject {
#objc func sayHello(){
//function
}
#objc func addX(){
//function
}
}
In my case I had forgotten to add:
#import "MyProject-Swift.h"
Into the obj c file.
In Objective C we can declare a delegate this way
#property (nonatomic, weak) id<SomeProtocol> delegate
and in swift
weak var delegate : SomeProtocol?
but in Objective C we can force the delegate to be of a certain class:
#property (nonatomic, weak) UIViewController<SomeProtocol> delegate
how i do this in swift?
Swift requires you to know the size of the type at compile time, so you would need to make your class generic to your delegate type:
protocol SomeProtocol: class {}
class SomeClass<T: UIViewController> where T: SomeProtocol {
weak var delegate : T?
}
There's another option, if you really don't care about it being constrained to a certain type, but to a certain interface, you could describe the UIViewController via another protocol that exposes the methods you need of it.
protocol UIViewControllerProtocol: class,
NSObjectProtocol,
UIResponderStandardEditActions,
NSCoding,
UIAppearanceContainer,
UITraitEnvironment,
UIContentContainer,
UIFocusEnvironment {
var view: UIView! { get set }
func loadView()
func loadViewIfNeeded()
var viewIfLoaded: UIView? { get }
}
extension UIViewController: UIViewControllerProtocol {}
protocol SomeProtocol: class {}
class SomeClass {
weak var delegate : SomeProtocol & UIViewControllerProtocol?
}
This will let you use many of the methods and properties in a UIViewController, but it doesn't really constraint your delegate to be a UIViewController because any other object could implement this protocol and be used instead.
PS: this is Swift 3.0
I have a Objective C class that has methods that look like this:
#class Client;
#protocol ClientDelegate <NSObject>
#optional
-(void) receivedMessageFromClient : (id) message;
#end
#interface Client : NSObject
+(id) setup;
#property(nonatomic, strong) id <ClientDelegate> clientDelegate;
// more method declarations below
I am implementing the ClientDelegate in my Swift class like this:
class HomeViewController: UIViewController, ClientDelegate {
var client : AnyObject?
var delegate: ClientDelegate?
override func viewDidLoad() {
super.viewDidLoad()
client = Client.setup() as! Client
client.clientDelegate = self
}
func receivedMessageFromClient(message: AnyObject) {
print("Message recieved: \(message)")
}
}
This is giving me a compiling error:
Cannot assign to property: 'self' is immutable
When I remove the lines
client = Client.setup() as! Client
client.clientDelegate = self
the code compiles and calls the method in the Client class that in turns sends a message to receivedMessageFromClient, but the method is not called in HomeViewController. It seems that everything is setup with the exception of assigning self to be the delegate.
Check out my comments on your question but this is a basic example of an Objective-C delegate implemented in Swift:
Objective-C:
#protocol MyDelegate <NSObject>
#optional
- (void)canImplementThis:(int)aVar;
#required
- (BOOL)needToImplementThis;
#end
#interface MyClass : NSObject
#property (nonatomic, weak) id<MyDelegate> delegate;
#end
Swift:
class SwiftClass : UIViewController, MyDelegate {
var myObj : MyClass?
override func viewDidLoad() {
super.viewDidLoad()
myObj = MyClass()
myObj?.delegate = self
}
// MARK: - <MyDelegate>
func canImplementThis(aVar : Int) {
print("Called: \(aVar))
}
func needToImplementThis() -> Bool {
return false
}
}
Forgive any typos I typed this out straight into SO.
Had to change Int to Int32 for some reason so it fix the invalid selector issue, with Int32 on the swift side but then worked
My intention is to create a generic class in Swift which conforms to an Objective-C protocol:
The class is:
class BaseViewFactoryImpl<T> : NSObject, BaseView {
func getNativeInstance() -> AnyObject {
return String("fsd")
}
}
The protocol BaseView is:
#protocol BaseView < NSObject >
- (id)getNativeInstance;
#end
The compiler tells me:
Type 'BaseViewFactoryImpl<T>' does not conform to protocol 'BaseView'
If I delete <T> then there is no error.
What is wrong here? How can I get the correct generic class implementation?
//BaseViewFactory.swift
class BaseViewFactoryImpl<T> : NSObject, BaseView {
func getNativeInstance() -> AnyObject {
return String("fsd")
}
//BaseViewProtocol.h
#protocol BaseView <NSObject>
- (id)getNativeInstance;
#end
//BridgingHeader.h
#import "BaseClassProtocol.h"
Your code should work. Have you created the bridging header to import the obj-C protocol file?
Starting with Swift 3, you need to use Any, not AnyObject, to represent generic id values coming from Objective-C.
This builds with no errors:
class BaseViewFactoryImpl<T> : NSObject, BaseView {
func getNativeInstance() -> Any {
return "fsd"
}
}
If you create a new generic view model, when you try to create any subclass of the generic view model, you need to declare the subclass as a generic class as well. It's kind of annoy.
For a better way, you can use typealias to declare the instance's type instead of using generic:
protocol BaseView {
typealias T
func getNativeInstance() -> T!
}
class StringViewModel : BaseView {
typealias T = String
func getNativeInstance() -> String! {
return T()
}
}