将 Objective-C 委托方法与 swift 文件混合

Posted

技术标签:

【中文标题】将 Objective-C 委托方法与 swift 文件混合【英文标题】:Mix Objective-C delegate method with swift file 【发布时间】:2018-10-25 08:56:20 【问题描述】:

我开始在 Objective-C 中开发这个应用程序。通过最近遇到的一些问题,我开始将 swift 用于一些功能。一切正常。现在我开始构建一个新功能并决定快速完成。我在一个只有 Swift 的项目中编写了代码,用于测试。在测试版本中一切正常,但在我的主项目中实现它时遇到了问题。

问题是我在委托文件中设置视图选项是这样的:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 

    window = UIWindow(frame: UIScreen.main.bounds)
    window?.makeKeyAndVisible()

    let layout = UICollectionViewFlowLayout()
    window?.rootViewController = UINavigationController(rootViewController: HomeController(collectionViewLayout: layout))

    return true

但是因为我的主要项目委托文件在 Objective-C 中,所以我不知道如何让它在我的 Swift 文件中工作在 Objective-C 项目中。我尝试在 viewDidLaunch 文件中设置视图。但这不起作用。 所以我问自己是否真的可以在objective-c的objective-c委托方法中为我的swift文件设置代码。但是对于我的项目,我想在 swift 文件中设置视图选项。所以这是我到目前为止所尝试的:

import UIKit

class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout 

var window: UIWindow?

override func viewDidLoad() 
    super.viewDidLoad()

    window = UIWindow(frame: UIScreen.main.bounds)
    window?.makeKeyAndVisible()

    let layout = UICollectionViewFlowLayout()
    window?.rootViewController = UINavigationController(rootViewController: HomeController(collectionViewLayout: layout))

    navigationItem.title = "Home"

    collectionView?.backgroundColor = UIColor.white

    collectionView?.register(VideoCell.self, forCellWithReuseIdentifier: "cellId")


//number of items
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return 10


override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)

    return cell


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    return CGSize(width: view.frame.width, height: 200)


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat 
    return 0



class VideoCell: UICollectionViewCell
override init(frame:CGRect)
    super.init(frame: frame)
    setupViews()


let thumbnailImageView: UIImageView = 
    let imageView = UIImageView()
    imageView.backgroundColor = UIColor.blue
    return imageView
()

let userProfileImageView: UIImageView = 
    let imageView = UIImageView ()
    imageView.backgroundColor = UIColor.green
    return imageView
()

let separatorView: UIView = 
    let view = UIView()
    view.backgroundColor = UIColor.black
    return view
()

let titleLabel: UILabel = 
    let label = UILabel()
    label.backgroundColor = UIColor.purple
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
()

let subtitleTextView: UITextView = 
    let textView = UITextView()
    textView.backgroundColor = UIColor.red
    textView.translatesAutoresizingMaskIntoConstraints = false
    return textView
()

func setupViews()
    addSubview(thumbnailImageView)
    addSubview(separatorView)
    addSubview(userProfileImageView)
    addSubview(titleLabel)
    addSubview(subtitleTextView)

    //Abstand zum Bildschirmrand (Blau)
    addConstraintsWithFormat(format: "H:|-16-[v0]-16-|", views: thumbnailImageView)

    //Grün
    addConstraintsWithFormat(format: "H:|-16-[v0(42)]", views: userProfileImageView)

    //vertical constraints / v0 = Blau höhe / v1 = Grün höhe / v2 = Linie höhe
    addConstraintsWithFormat(format: "V:|-32-[v0(75)]-8-[v1(44)]-16-[v2(1)]|", views: thumbnailImageView, userProfileImageView, separatorView)

    //Abtrennung zwischen Zellen /zweite Zeile wird in "Große Fläche" umgesetzt
    addConstraintsWithFormat(format: "H:|[v0]|", views: separatorView)

    //top constraint
    addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: thumbnailImageView, attribute: .bottom, multiplier: 1, constant: 8))
    //left constraint
    addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .left, relatedBy: .equal, toItem: userProfileImageView, attribute: .right, multiplier: 1, constant: 8))
    //right constraint
    addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .right, relatedBy: .equal, toItem: thumbnailImageView, attribute: .right, multiplier: 1, constant: 0))
    //height constraint
    addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 0, constant: 20))


    //top constraint
    addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .top, relatedBy: .equal, toItem: titleLabel, attribute: .bottom, multiplier: 1, constant: 4))
    //left constraint
    addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .left, relatedBy: .equal, toItem: userProfileImageView, attribute: .right, multiplier: 1, constant: 8))
    //right constraint
    addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .right, relatedBy: .equal, toItem: thumbnailImageView, attribute: .right, multiplier: 1, constant: 0))
    //height constraint
    addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 0, constant: 20))

    thumbnailImageView.frame = CGRect(x: 0, y: 0, width: 50, height: 50)



required init?(coder aDecoder: NSCoder) 
    fatalError("init(coder:) has not been implemented")



extension UIView 
func addConstraintsWithFormat(format: String, views: UIView...)
    var viewsDictionary = [String: UIView]()
    for (index, view) in views.enumerated()
        let key = "v\(index)"
        view.translatesAutoresizingMaskIntoConstraints = false
        viewsDictionary[key] = view
    

    addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: viewsDictionary))



【问题讨论】:

【参考方案1】:

一般到make swift classes available in objective-c.another question/answer on stack 对我也有帮助。

但是考虑一下根本的变化: 最好是在 swift 中创建一个新应用程序并通过 brindging header 连接到旧类。 documentation 真的很有帮助。

将来,在 swift 中添加新元素和使用新功能会更容易。

【讨论】:

【参考方案2】:

在视图控制器的viewDidLoad 内执行此操作根本不安全,您甚至不应该尝试。

这是因为viewDidLoad 有可能被执行多次

如果发生这种情况,它将在您的应用执行过程中将您的窗口和rootViewController 替换为新实例(在用户看来,就像应用已自行重置一样)。

您需要从应用委托初始化窗口和根控制器,别无选择。

但是,您仍然可以使用 Swift 编写大部分代码。

首先,在UIWindow 上创建一个 Swift 扩展,其中包含一个初始化和配置窗口的类函数,然后返回它。通过在声明中添加 @objc 来确保 Objective-C 可以访问该扩展:

@objc extension UIWindow 
    class func setRootHomeViewController() -> UIWindow 
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.makeKeyAndVisible()
        let layout = UICollectionViewFlowLayout()
        window.rootViewController = UINavigationController(rootViewController: HomeController(collectionViewLayout: layout))
        return window;
    

在您的 Objective-C 应用程序委托中,您需要导入您的 Swift 标头并调用您定义的新 UIWindow 类方法。将返回的窗口对象分配给应用委托的window 属性。

#import "YourProjectName-Swift.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 

    _window = [UIWindow setRootHomeViewController];

    return YES;


@end

这只是一行 Objective-C 代码,但这是必要的,因为应用程序委托是唯一安全的地方。

【讨论】:

以上是关于将 Objective-C 委托方法与 swift 文件混合的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中转换 Objective-C TOCropViewController 委托方法?

在 Swift 上委托 Objective-C 协议

将 Objective-C 协议委托作为参数传递

Objective-C编程 - 关于Block的几点

Swift与Objective-C中的命名方法

Objective-C:Swift Package 函数中的多参数方法语法