how to use .svg images in SwiftUI - swift

Can I only upload .png files into the assets folder? I get an error for .svg files.
I want to use it for the Image() component

You can use Macaw to display a SVG: https://github.com/exyte/Macaw
1) Add the Package via SPM to your project (the tag 0.9.5 actually doesn't work, you need to use the master branch)
2) Move your .svg-file to the project or create a new Data set in your Assets.xcassets and then add the svg file to this data set.
3) Use this code:
struct MyView: View {
var svgName: String
var body: some View {
SVGImage(svgName: self.svgName)
.frame(width: 50, height: 550)
}
}
struct SVGImage: UIViewRepresentable {
var svgName: String
func makeUIView(context: Context) -> SVGView {
let svgView = SVGView()
svgView.backgroundColor = UIColor(white: 1.0, alpha: 0.0) // otherwise the background is black
svgView.fileName = self.svgName
svgView.contentMode = .scaleToFill
return svgView
}
func updateUIView(_ uiView: SVGView, context: Context) {
}
}

You can import svg images too in XCAssets.
Go to XCAssets, click on + button.
Refer this image
Click on import & select svg image.
Now to load & display that image use below code :
Image(uiImage: UIImage(named: "imageName")!)

I've created a simple tool, which converts SVG code into SwiftUI's Shape object. It is very limited for now (in particular it only supports straight lines and cubic curves, single path SVGs and shapes that are filled with one color), but you can still use it for simple situations.
Here's the link to the tool, and the repository, in case you'd want to contribute and make it support all SVG features.

You can use pdf's for vectors in the asset catalog (select single scale and preserve vector data in the inspector on the right). You cannot directly use SVG's in the asset catalog, but with Xcode 11 you actually can use SVG's for symbols. Apple has a detailed guide on how to make your own SVG symbols here.

Related

Dynamically use all SwiftUI Views included in a Swift Package in an App using this package

I have been creating SwiftUI Views in a Swift Package such as this one:
public struct DottedLoaderView: View {
#State private var isOn = false
let numOfDots: Int
let dotWidth: CGFloat
let dotsColor: Color
public init(numOfDots: Int, dotWidth: CGFloat, dotsColor: Color){
self.numOfDots = numOfDots
self.dotWidth = dotWidth
self.dotsColor = dotsColor
}
public var body: some View {
HStack{
ForEach(0..<numOfDots){ i in
DottedLoaderDotView(dotWidth: dotWidth, dotsColor: dotsColor, delay: Double(i) / 2)
}
}
}
I want to make an application which will use this Package as a catalog of the Views I have implemented in the Package. I would like to display all the available Views from the package inside of a ListView in the catalog app to achieve a result similar to this:
The list row is calling the View struct from the Package. This works fine with a low amount of Views, however, I want to have hundreds of them in the future and would ideally want the app to dynamically display all the available Views and be able to iterate over them inside of a ForEach loop in the body. Is there any convenient approach that comes to mind on how to essentially "look through the Package and fetch all the Views available and have them as some variable ideally in a list?" so I can treat them as data to feed into a ForEach loop? I apologise if this is a really vague question.

Correct icon size for Big Sur preference panels

When building my app for macOS Big Sur, and switching my preference panel toolbar style to .preference, the icons just don't seem to size correctly.
No matter if I use #1x/#2x PNG variants, or universal PDF vector images with a single scale, my icons always turn out larger and blurry compared to using a SF Symbol icon.
Now that my app needs to be backwards compatible with 10.13 I can't use symbol images.
Here is what my toolbar looks like
What is the correct way to get properly sized icons in a preference panel toolbar?
Do I need to include a margin in my vector assets? If so, what is the correct point size?
I got a good result with 26x26px vector PDF. The icon size was 22x22px with the margins but the icon was blurry. Enabling Preserve Vector Data option did the trick
You can set it under the Image Set settings:
Vector images don't seem to go well with NSToolbar. I created an NSImage extension for them to manually create a toolbar friendly image.
extension NSImage {
convenience init?(namedVectorToolbarImage name: String) {
guard let image = NSImage(named: name) else {
return nil
}
self.init()
let small = image.resized(to: .init(width: 24, height: 24))
let regular = image.resized(to: .init(width: 32, height: 32))
let reps = regular.representations + small.representations
isTemplate = true
reps.forEach {
addRepresentation($0)
}
}
The resize() method here is just another extension for producing a resized image. On top of that I set NSToolbar.sizeMode to small if the system is not (below) macOS 11.0.

Pass image name to custom Image subclass in SwiftUI

I am trying to accomplish something that looks pretty simple: I have a custom Image subclass in SwiftUI which I’m using for convenience, and I would like to use it in my app. My end goal is to use the OnboardingThumbnailImage in other views and simply pass an image name to the constructor.
import SwiftUI
struct OnboardingThumbnailImage: View {
var imageName: String
var body: some View {
Image(imageName)
.resizable()
.scaledToFit()
.frame(width: 50, height: 50)
.foregroundColor(Colors.tintColor)
}
}
struct OnboardingThumbnailImage_Previews: PreviewProvider {
static var previews: some View {
OnboardingThumbnailImage(imageName: "?????")
}
}
How can I accomplish this? The compiler requires me to specify a value inside OnboardingThumbnailImage_Previews so I have no clue. I have looked into Bindings but I don't need a 'two-way street' between the views, so I'm not sure.
Can I instead just perhaps leave Image() with no arguments inside, in order to inherit the default Image constructor? If I leave Image() I get an error: Cannot invoke initializer for type 'Image' with no arguments.
This is SwiftUI's way of asking you
What image do you want to show when this View is previewed?
You can only preview SwiftUI views on macOS Catalina, so if you are not using Catalina (like me), then this feature is not very relevant to you.
You are supposed to put the image that you want to see in the previews in the ???? bit. If you are not using Catalina, or you just don't want to preview it, you can just delete the whole OnboardingThumbnailImage_Previews struct.
Also note that you can't "subclass" another view in SwiftUI. All you can do is composition, which is what you have done here. SwiftUI's design favours composition over inheritance. You can find explanations of this concept in these pages: 1, 2.
In your struct view, the variable imageName needs value for initialization. as it should be string, because you define a string variable and use it in your Image. To ignore the error you can set an empty value to your variable to ignore the requirement to init in view construction
var imageName: String = ""

FontAwesome UITabBarItem not displaying correctly

I am trying to create icons for each of my view controllers, but it is creating a question mark block as if it does not understand the image
let settings = UINavigationController(rootViewController: SettingsController())
let individual = IndividualAthleteController()
var items: [FontAwesome: UIViewController] = [.slidersH: settings,.user: individual]
for (iconString, viewController) in items {
let icon = UIImage.fontAwesomeIcon(name: iconString, style: .brands, textColor: .black, size: CGSize(width: 30, height: 30))
let tabBar = UITabBarItem(title: viewController.title, image: icon, selectedImage: icon)
items[iconString]!.tabBarItem = tabBar
}
viewControllers = Array(items.values)
It is creating this image for each viewController:
What is the problem?
Make sure you're importing .ttf file into your project and set the target of the files. Then, make sure you're updating your plist:
Import .ttf file into you project simply by draggin&drop, check "copy if needed" (Es. fontawesome-webfont.ttf)
Select you .ttf file and in the inspector select the icon above similar to a sheet, then in the section below (Target Membership) select your project as target if not
Finally check you plist and add the key "Fonts provided by application in your", and as item0 (it is an array) put the name of your fonts as value (es. fontawesome-webfont.ttf )
Refer to this link to see images of the previous instructions
If you're using this library Vaberer/Font-Awesome-Swift, make sure you're doing the same as the above steps
NOTE: If you're not using the Vaberer library, give it a chance
Use .solid instead of .brands. Brands uses just the font awesome brands library, for using icons like apple, github, etc.

NSCollectionView Grid of Files Displayed with Icons from Finder's Preview

I am having trouble trying to create an NSCollectionView with icons that are taken from Finder's icon preview.
Ideally, if the file is a dmg it should display the hard drive icon, an mp3 should be the mp3 icon and a photo should display what the photo looks like itself.
There is one post that addresses this issue:
Is it possible to get programmatic access to finder preview images?
But the code is old and does not compile. I am also using Swift in this project.
I used this post to setup a template for my project:
Is there a way to set up a NSCollectionView programmatically in Swift?
Which adds a subview to NSView. If this can be modified to implement the icons, that would be ideal.
In addition I am using a NSFileManager.defaultManager to get the contents of a folder, and would like to display those contents in the collection view.
I am new to developing on the Mac platform and have more experience with iOS, specific answers are much appreciated, thanks!
Update, I was able to figure out how to use what Finder uses with the Quicklook library that Apple provides. Here is part of the code that I used. Note that if you are trying to use Quicklook on a file that does not have a preview, then there will be an error, so in those cases I just used the iconForFile function (below this code block)
let tmp = QLThumbnailImageCreate(kCFAllocatorDefault, tmpURL , CGSize(width: 64, height: 64), nil)
tmpImage = tmp.takeUnretainedValue()
tmpIcon = NSImage(CGImage: tmpImage!, size: NSSize(width: 64, height: 64))
Original:
I was able to find (almost) what I was looking for.
In the NSWorkspace class there is a method
func iconForFile(_ fullPath: String) -> NSImage
That will return an NSImage of the icon. Unfortunately, I do not see a way to get a preview of the image if it is a picture. Nor do I see a way to make the picture larger (only 32x32 pixels).
Hopefully this answer will help others that encounter this problem!
I've used your code from your answer but I noticed that it did not keep the file's aspect ratio and the low res version would just be the downsampled high res version. So based on your answer, here's my solution:
let imageLowResRef = QLThumbnailImageCreate(kCFAllocatorDefault, url as CFURL , CGSize(width: 32, height: 32), nil)
guard imageLowResRef != nil else {
return NSWorkspace.shared.icon(forFile: url.path)
}
let image = NSImage()
let imageLowRes = imageLowResRef!.takeUnretainedValue()
image.addRepresentation(NSBitmapImageRep(cgImage: imageLowRes))
let imageHighResRef = QLThumbnailImageCreate(kCFAllocatorDefault, url as CFURL , CGSize(width: imageLowRes.width*2, height: imageLowRes.height*2), nil)
let imageHighRes = imageHighResRef!.takeUnretainedValue()
image.addRepresentation(NSBitmapImageRep(cgImage: imageHighRes))
Edit: If you add let options = [kQLThumbnailOptionIconModeKey: true] as CFDictionary, you get to the options parameter, you get the preview rendered as icons.