Home > Net >  Subclassing UIView from Kotlin Native
Subclassing UIView from Kotlin Native

Time:09-22

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) {
    }

}

  • Related