UIKit is designed to be used through subclasses and overridden methods.
Typically, the drawRect
objective-C method of UIView is implemented like this in 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)
}
}
Unfortunately, the UIKit import in Kotlin defines these functions as extensions function that cannot be overridden.
Did anybody succeed in subclassing an UIView from Kotlin through a custom cinterop
configuration?
CodePudding user response:
You can just skip override
, ie overload completely, because default implementation does nothing (that is why it is in extension).
For instance, next is also valid:
class SmileView: UIView {
func draw(_ rect: CGRect) {
let smile = ":)" as NSString
smile.draw(in: rect, withAttributes: nil)
}
}
Here is a documentation of interface:
Summary
Draws the receiver’s image within the passed-in rectangle. Declaration
func draw(_ rect: CGRect) Discussion
The default implementation of this method does nothing. Subclasses that use technologies such as Core Graphics and UIKit to draw their view’s content should override this method and implement their drawing code there.
CodePudding user response:
So we managed to make it work.
1. Add a cinterop configuration task in the build.gradle.kts
kotlin {
android()
ios {
binaries {
framework {
baseName = "shared"
}
}
compilations.getByName("main") {
val uikit by cinterops.creating {
}
}
}
2. Add a `src/nativeinterop/cinterop/uikit.def` file.
package = demo.cinterop
language = Objective-C
---
#import <Foundation/Foundation.h>
#import <UIKit/UIView.h>
@protocol UIViewWithOverrides
- (void) drawRect:(CGRect)aRect;
- (void) layoutSubviews;
@end
3. Create a custom UIView class
The class extends the UIView from UIKit and implements the previously created UIViewWithOverridesProtocol (the suffix is automatically added)
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. Use it from 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) {
}
}