icerockdev / moko-resources

Resources access for mobile (android & ios) Kotlin Multiplatform development
https://moko.icerock.dev/
Apache License 2.0
1.11k stars 123 forks source link

Add svg images support #29

Closed Alex009 closed 1 year ago

Alex009 commented 4 years ago

https://github.com/vdmeer/svg2vector may help

chris-hatton commented 3 years ago

My 2c; but I very strongly disagree with these various calls to add GIF/SVG support - this goes outside the scope of what MokoResources should be doing, which is abstract the common resource access across platforms. iOS/Android don't have consistent SVG or GIF support out-of-the-box, and so there's nothing for Moko to abstract against.

What MokoResources does do is provide raw file access. We're doing this in our project to load SVG's with AndroidSVG / SVGKit and it's working great.

Alex009 commented 3 years ago

vector graphics supported by both platforms out of box. for android it VectorDrawable and for ios it pdf. goal is implement support of this types of graphics. Or by auto-conversion tool with single svg source - generate vectorDrawable for android from svg and generate pdf for ios, from svg.

about gif support i agree with you

Alex009 commented 3 years ago

can be helpful https://github.com/jcraane/kmm-images

msa1422 commented 2 years ago

SVG files are supported indirectly. Workaround is simple. However, direct use of SVG (without conversion to XML) can be taken as a limitation.

// Coil + Compose val coilPainter = rememberAsyncImagePainter( model = MR.assets.your_svg_image.toAndroidAssetUri() ) Image( painter = coilPainter, contentDescription = "Some svg image" )


- On iOS side, use SDWebImage with [SVGDecoder](https://github.com/SDWebImage/SDWebImageSVGCoder) for loading images

// Swift UI WebImage(url: URL(fileURLWithPath: MR.assets.shared.nsBundle .path(forResource:"your_svg_image, ofType: "svg") ?? "") )



[Here](https://github.com/msa1422/KMM-Arch-PetSearch) is a sample project that implements this technique successfully, plus much more.
racka98 commented 2 years ago
// Extension fun
fun AssetResource.toAndroidAssetUri() = "file:///android_asset/$originalPath"

// Coil + Compose
val coilPainter = rememberAsyncImagePainter(
    model = MR.assets.your_svg_image.toAndroidAssetUri()
)
Image(
    painter = coilPainter,
    contentDescription = "Some svg image"
)

Just adding on top of this. For Compose Desktop you can use:

@Composable
fun assetPainter(resource: AssetResource): Painter =
    androidx.compose.ui.res.painterResource(resourcePath = "files/${resource.originalPath}")

All stuff inside assets folder are placed in files directory for generated desktop (jvm) resources.

racka98 commented 1 year ago

Hey @msa1422 , any ideas why my assets aren't included in my apk? My setup is basically the same as yours. I have also added this in the module containing the resources:

sourceSets["main"].apply {
    assets.srcDir(File(buildDir, "generated/moko/androidMain/assets"))
    res.srcDir(File(buildDir, "generated/moko/androidMain/res"))
}

Edit: NVM, I Invalidated my IDE cache and it worked. But it doesn't seem to load vector drawables on Android (works on Compose desktop), only pure SVG

msa1422 commented 1 year ago

Hi @racka98. The implementation is working in my projects for the files with .svg extension placed under Shared MR assets folder.

I haven't tried placing the vector drawables (.xml) files under the MR assets folders yet. My solution works only for KMM. I also haven't tried out KMP and compose desktop yet.

Probably, I am not able to correctly infer "it" when you said "But doesn't it seem to load vector drawables on Android". Please tell me a little bit more about the issue.

racka98 commented 1 year ago

If I place them (vector drawables) inside the assets folder, accessing them in android sources using R.drawable.xxx works (resolves on IDE) but upon compilation it throws the Unresolved Reference: xxx error.

But I think I'll just convert them all to SVG since it will still be a hassle to access them on iOS if they are Vector drawables.

SiavashB commented 1 year ago

// Coil + Compose val coilPainter = rememberAsyncImagePainter( model = MR.assets.your_svg_image.toAndroidAssetUri() ) Image( painter = coilPainter, contentDescription = "Some svg image" )

Is there anyway to do this without coil?

msa1422 commented 1 year ago

Out of the box solutions for Compose ImagePainter requires resourceId. Android assets do not have any id associated with them. I believe, as of now, Coil is the only way to load images from assets by using a URI of the asset image. Edit - An Image loading library basically. Glide will also do.

archiegq21 commented 1 year ago

@msa1422 I had the exact experience with @racka98. It doesn't load the SVG. Do you have any idea why?

@racka98 Did you find any solution?

racka98 commented 1 year ago

@racka98 Did you find any solution?

I have this in commonMain

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import dev.icerock.moko.resources.AssetResource

@Composable
expect fun painterResource(resource: AssetResource): Painter

On Android I now use Coil to load the Assets:

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import coil.compose.rememberAsyncImagePainter
import dev.icerock.moko.resources.AssetResource

fun AssetResource.toAndroidAssetUri() = "file:///android_asset/$originalPath"

@Composable
actual fun painterResource(resource: AssetResource): Painter =
    rememberAsyncImagePainter(model = resource.toAndroidAssetUri())

On Desktop (Compose) I use this:

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import dev.icerock.moko.resources.AssetResource

@Composable
actual fun painterResource(resource: AssetResource): Painter =
    androidx.compose.ui.res.painterResource(resourcePath = "files/${resource.originalPath}")

Pay attention to the imports on Desktop and Android.

msa1422 commented 1 year ago

After upgrading MokoResources to v0.21.1, I have run into a bizarre problem. I have upgraded to the latest version in 3 projects. PetSearch, and 2 other projects. SVG AssetResource seems to be working fine in one of my client's project. However, in the PetSearch mentioned above and an another personal project, SVG AssetResource is not working. Project setup is identical in all 3 projects. Upon some investigation, I found out that the method AssetResource.readText(context) is printing the SVG file content in the client project. However in Petsearch, it is throwing java.io.FileNotFoundException. Coil is also throwing the same java.io.FileNotFoundException. @Alex009, I have created this branch for you to investigate. @racka98 Could you please upgrade the MokoResources version and see if this issue shows up in your project?

UPDATE: Adding new svg (arrow_left.svg) in assets in client project, is throwing java.io.FileNotFoundException for the new resource (arrow_left.svg).

UPDATE: I added a couple of modules in the client project, the new resource (arrow_left.svg) is not throwing exception anymore. After repeated clean builds, all 3 projects are showing the icons, and not throwing any exception. With that said, whenever I add a new svg, Coil throws java.io.FileNotFoundException.

Alex009 commented 1 year ago

Also can be useful https://github.com/ravibhojwani86/Svg2VectorAndroid

terrakok commented 1 year ago

There is another interesting way: https://github.com/DevSrSouza/svg-to-compose

Alex009 commented 1 year ago

will be released in 0.22.0