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。在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并实现了之前创建的UIViewWithOverrides协议(后缀自动添加)
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) {
}
}
上面的答案非常棒,在我需要重写updateConstraints()之前,它对我非常有用——它必须调用super.updateConstraints()。如果没有这个,我就会得到运行时错误,而且我发现没有办法通过Kotlin <->快速互操作(现在我很确定这是不可能的)。
所以,相反,我放弃了在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部分是先编译的。