moko-resources icon indicating copy to clipboard operation
moko-resources copied to clipboard

Add svg images support

Open Alex009 opened this issue 4 years ago • 4 comments

Alex009 avatar Jan 24 '20 06:01 Alex009

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

Alex009 avatar Jan 24 '20 06:01 Alex009

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.

chris-hatton avatar Feb 19 '21 02:02 chris-hatton

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 avatar Feb 19 '21 02:02 Alex009

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

Alex009 avatar Jul 29 '21 12:07 Alex009

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

  • Create folder MR/assets.
  • Add .svg files in the assets folder.
  • Now on Android side, create an extension for AssetResource and use Coil with SVGDecoder to load images.
// 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"
)
  • On iOS side, use SDWebImage with SVGDecoder for loading images
// Swift UI
WebImage(url: URL(fileURLWithPath: MR.assets.shared.nsBundle
    .path(forResource:"your_svg_image, ofType: "svg") ?? "")
)

Here is a sample project that implements this technique successfully, plus much more.

msa1422 avatar Oct 14 '22 04:10 msa1422

// 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 avatar Nov 20 '22 10:11 racka98

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

racka98 avatar Nov 21 '22 19:11 racka98

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.

msa1422 avatar Nov 22 '22 05:11 msa1422

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.

racka98 avatar Nov 22 '22 05:11 racka98

// 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?

SiavashB avatar Jan 04 '23 22:01 SiavashB

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.

msa1422 avatar Jan 06 '23 06:01 msa1422

@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?

archiegq21 avatar Mar 09 '23 06:03 archiegq21

@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.

racka98 avatar Mar 09 '23 08:03 racka98

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.

msa1422 avatar Mar 27 '23 05:03 msa1422

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

Alex009 avatar Apr 03 '23 18:04 Alex009

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

terrakok avatar Apr 07 '23 23:04 terrakok

will be released in 0.22.0

Alex009 avatar Apr 25 '23 06:04 Alex009