从 Kotlin Native 继承 UIView
Posted
技术标签:
【中文标题】从 Kotlin Native 继承 UIView【英文标题】:Subclassing UIView from Kotlin Native 【发布时间】:2021-11-14 20:34:13 【问题描述】:UIKit 旨在通过子类和重写方法使用。
通常,UIView 的drawRect
objective-C 方法在 SWIFT 中是这样实现的:
import UIKit
import Foundation
class SmileView: UIView
override func draw(_ rect: CGRect)
super.draw(rect)
let smile = ":)" as NSString
smile.draw(in: rect, withAttributes: nil)
不幸的是,Kotlin 中的 UIKit 导入将这些函数定义为无法覆盖的扩展函数。
是否有人通过自定义 cinterop
配置成功地从 Kotlin 子类化 UIView?
【问题讨论】:
【参考方案1】:所以我们设法让它发挥作用。
1.在build.gradle.kts中添加cinterop配置任务
kotlin
android()
ios
binaries
framework
baseName = "shared"
compilations.getByName("main")
val uikit by cinterops.creating
2.添加一个`src/nativeinterop/cinterop/uikit.def`文件。
package = demo.cinterop
language = Objective-C
---
#import <Foundation/Foundation.h>
#import <UIKit/UIView.h>
@protocol UIViewWithOverrides
- (void) drawRect:(CGRect)aRect;
- (void) layoutSubviews;
@end
3。创建自定义 UIView 类
该类从UIKit扩展了UIView,实现了之前创建的UIViewWithOverridesProtocol(后缀自动添加)
package demo
import demo.cinterop.UIViewWithOverridesProtocol
import kotlinx.cinterop.*
import platform.CoreGraphics.*
import platform.UIKit.*
@ExportObjCClass
class MyView() : UIView(frame = CGRectMake(.0, .0, .0, .0)), UIViewWithOverridesProtocol
override fun layoutSubviews()
println("layoutSubviews")
setNeedsDisplay()
override fun drawRect(aRect: CValue<CGRect>)
val rectAsString = aRect.useContents
"" + this.origin.x + ", " + this.origin.y + ", " + (this.origin.x +this.size.width) + ", " + (this.origin.y +this.size.height)
println("drawRect:: Rect[$rectAsString]")
val context: CPointer<CGContext>? = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, 2.0)
val components = cValuesOf(0.0, 0.0, 1.0, 1.0)
CGContextSetFillColor(context, components)
val square = CGRectMake(100.0, 100.0, 200.0, 200.0)
CGContextFillRect(context, square)
fun createMyView(): UIView = MyView()
4. 在 Swift 中使用它
struct ChartView: View
var body: some View
VStack
Text("Chart View")
MyView()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
struct ChartView_Previews: PreviewProvider
static var previews: some View
ChartView()
struct MyView: UIViewRepresentable
func makeUIView(context: Context) -> UIView
UIChartViewKt.createMyView()
func updateUIView(_ uiView: UIView, context: Context)
【讨论】:
请注意,您可以使用cValue CGRectZero
代替CGRectMake(.0, .0, .0, .0)
感谢@PhilipDukhov。
用@ObjCAction 注释来注释layoutSubviews 会不会更容易?【参考方案2】:
上面的答案很棒,在我需要重写 updateConstraints() 之前它对我很有帮助——它必须调用 super.updateConstraints()。没有它,我会遇到运行时错误,并且我找不到如何通过 Kotlin Swift 互操作来执行该调用(现在我有理由确定这真的不可能)。
因此,我放弃了尝试在 Swift 中对自定义 UIView 进行子类化,而只专注于从 Kotlin/Native 实际实例化它(以便向它传递所需的数据):
class CustomView : UIView
/* Data we need to use from the Kotlin Code */
lazy var kotlinClass: KotlinClass? = nil
... init etc. ...
override func updateConstraints()
... my stuff ...
super.updateConstraints()
override func draw(_ rect: CGRect)
... call the kotlinClass' methods as you need ...
并实现了一个工厂函数来实例化它:
func customViewFactory(kotlinClass: KotlinClass) -> UIView
return CustomView(kotlinClass: kotlinClass)
然后在应用启动初期,我将这个工厂函数传递给 Kotlin/Native 代码,如下所示:
KotlinClass.Companion.shared.setCustomViewFactory(factory: customViewFactory(kotlinClass:))
在项目的 Kotlin 部分(实际上是在 Swift 部分之前编译的),看起来是这样的:
class KotlinClass
companion object
/* To be used where I want to instantiate the custom UIView from the Kotlin code. */
lateinit var customViewFactory: (kotlinClass: KotlinClass) -> UIView
/* To be used early during the startup of the app from the Swift code. */
fun setCustomViewFactory(factory: (kotlinClass: KotlinClass) -> UIView)
customViewFactory = factory
当我想在 Kotlin 代码中实例化自定义 UIView 时,我只需调用:
val customView = customViewFactory(this)
然后我可以在 Kotlin 部分中根据需要使用这个 customView,即使 Kotlin 部分是先编译的。
【讨论】:
以上是关于从 Kotlin Native 继承 UIView的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin/Native 无法导入 io.ktor.network.selector.ActorSelectorManager
如何使用 Kotlin 同时定位 JVM/Native 和 Android