https://kotlinlang.org logo
#compose-ios
Title
# compose-ios
b

brandonmcansh

11/07/2023, 1:28 PM
Getting a
EXC_BAD_ACCESS
when updating a UIEdgeInsets 🧵
created a subclass of UILabel to provide contentPadding
Copy code
@ExperimentalForeignApi
class UIPaddingLabel(rect: CValue<CGRect>) : UILabel(rect) {
    
    constructor(): this(CGRectZero.readValue())

    private var textEdgeInsets = UIEdgeInsetsZero
        set(value) {
            field = value
            invalidateIntrinsicContentSize()
        }

    override fun drawRect(rect: CValue<CGRect>) {
        super.drawRect(
            UIEdgeInsetsInsetRect(
                rect,
                UIEdgeInsetsMake(
                    <http://textEdgeInsets.top|textEdgeInsets.top>,
                    textEdgeInsets.left,
                    textEdgeInsets.bottom,
                    textEdgeInsets.right
                )
            )
        )
    }

    var paddingLeft: CGFloat
        get() = textEdgeInsets.left
        set(value) {
            textEdgeInsets.left = value
        }

    var paddingTop: CGFloat
        get() = <http://textEdgeInsets.top|textEdgeInsets.top>
        set(value) {
            <http://textEdgeInsets.top|textEdgeInsets.top> = value
        }

    var paddingRight: CGFloat
        get() = textEdgeInsets.right
        set(value) {
            textEdgeInsets.right = value
        }

    var paddingBottom: CGFloat
        get() = textEdgeInsets.bottom
        set(value) {
            textEdgeInsets.bottom = value
        }

    var paddingHorizontal: CGFloat
        get() = (textEdgeInsets.left) / 2 + (textEdgeInsets.right / 2)
        set(value) {
            textEdgeInsets.left = value
            textEdgeInsets.right = value
        }

    var paddingVertical: CGFloat
        get() = (<http://textEdgeInsets.top|textEdgeInsets.top>) / 2 + (textEdgeInsets.bottom / 2)
        set(value) {
            <http://textEdgeInsets.top|textEdgeInsets.top> = value
            textEdgeInsets.bottom = value
        }
}
the
EXC_BAD_ACCESS (code=2, address=0x1859da9c8)
is thrown when setting paddingHorizontal when creating my label like so:
Copy code
val label = UIPaddingLabel().apply {
        backgroundColor = UIColor.whiteColor
        text = "$$price"
        font = UIFont.systemFontOfSize(13.0, UIFontWeightSemibold)

        layer.apply {
            cornerRadius = 10.0
            masksToBounds = true
            shadowColor = UIColor.blackColor.CGColor
            shadowRadius = 2.0
            shadowOpacity = 0.2f
            shadowOffset = CGSizeMake(1.0, 2.0)
        }
        paddingHorizontal = 16.0
        paddingVertical = 8.0
        sizeToFit()
    }
l

Landry Norris

11/07/2023, 3:24 PM
The first thing I see is that you seem to be setting fields of the global UIEdgeInsetsZero, but you may be better off using UIEdgeInsetsMake to create a separate instance. I doubt that changing the global zero value would lead to EXC_BAD_ACCESS, but it may be in a read-only part of memory?
b

brandonmcansh

11/07/2023, 3:42 PM
Yea it looks like that was the issue
Using Make() and using get/set to use getPointer(MemScope()).pointed
l

Landry Norris

11/07/2023, 3:46 PM
Out of curiosity, why is the getPointer/pointed needed? Not at a computer right now, so can't easily check the full type. Does make not produce a CValue?
b

brandonmcansh

11/07/2023, 3:46 PM
make returns a CValueUIEdgeInsets
and accessing left/top/right/bottom isn't accessible without direct access to the UIEdgeInsets
actully looks like I can `
Copy code
get() = textEdgeInsets.useContents { return top }
1
l

Landry Norris

11/07/2023, 3:48 PM
Got it. I remember there being a useContents or similar method. You'll want to avoid making a copy to modify. I think useValue prevents a copy, and pointed doesn't.
b

brandonmcansh

11/07/2023, 3:49 PM
thanks for questioning it
made me check it out further
Copy code
@OptIn(BetaInteropApi::class)
@ExperimentalForeignApi
@ExportObjCClass
class UIPaddingLabel(rect: CValue<CGRect>) : UILabel(rect) {

    constructor(): this(CGRectZero.readValue())

    private var textEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)
        set(value) {
            field = value
            invalidateIntrinsicContentSize()
        }

    override fun textRectForBounds(
        bounds: CValue<CGRect>,
        limitedToNumberOfLines: NSInteger
    ): CValue<CGRect> {
        val insetRect = UIEdgeInsetsInsetRect(
            bounds,
            textEdgeInsets
        )
        val textRect = super.textRectForBounds(insetRect, limitedToNumberOfLines)
        val invertedInsets = textEdgeInsets.useContents {
            UIEdgeInsetsMake(
                top = -top,
                left = -left,
                right = -right,
                bottom = -bottom
            )
        }


        return UIEdgeInsetsInsetRect(textRect, invertedInsets)
    }

    override fun drawTextInRect(rect: CValue<CGRect>) {
        super.drawTextInRect(
            UIEdgeInsetsInsetRect(
                rect,
                textEdgeInsets
            )
        )
    }


    var paddingLeft: CGFloat
        get() = textEdgeInsets.useContents { return left }
        set(value) {
            textEdgeInsets = textEdgeInsets.copy { left = value }
        }

    var paddingRight: CGFloat
        get() = textEdgeInsets.useContents { return right }
        set(value) {
            textEdgeInsets = textEdgeInsets.copy { right = value }
        }

    var paddingTop: CGFloat
        get() = textEdgeInsets.useContents { return top }
        set(value) {
            textEdgeInsets = textEdgeInsets.copy { top = value }
        }

    var paddingBottom: CGFloat
        get() = textEdgeInsets.useContents { return bottom }
        set(value) {
            textEdgeInsets = textEdgeInsets.copy { bottom = value }
        }
}
updated class for those following along
3 Views