Convert Swiftui View to Nsimage

Convert SwiftUI View to NSImage

View+Image.swift

import SwiftUI

extension View {

func renderAsImage() -> NSImage? {
let view = NoInsetHostingView(rootView: self)
view.setFrameSize(view.fittingSize)
return view.bitmapImage()
}

}

NoInsetHostingView.swift

import SwiftUI

class NoInsetHostingView<V>: NSHostingView<V> where V: View {

override var safeAreaInsets: NSEdgeInsets {
return .init()
}

}

NSView+Image.swift

public extension NSView {

func bitmapImage() -> NSImage? {
guard let rep = bitmapImageRepForCachingDisplay(in: bounds) else {
return nil
}
cacheDisplay(in: bounds, to: rep)
guard let cgImage = rep.cgImage else {
return nil
}
return NSImage(cgImage: cgImage, size: bounds.size)
}

}

I'm not sure exactly why NoInsetHostingView is needed, but with just a normal NSHostingView the image has undesired insets, and this fixes it.

How can you turn a macOS SwiftUI view into an image?

In macOS same approach can be used. NSHostingController is analog of UIHostingController. Also to get it drawn the view should be added to some window:

extension View {
func snapshot() -> NSImage? {
let controller = NSHostingController(rootView: self)
let targetSize = controller.view.intrinsicContentSize
let contentRect = NSRect(origin: .zero, size: targetSize)

let window = NSWindow(
contentRect: contentRect,
styleMask: [.borderless],
backing: .buffered,
defer: false
)
window.contentView = controller.view

guard
let bitmapRep = controller.view.bitmapImageRepForCachingDisplay(in: contentRect)
else { return nil }

controller.view.cacheDisplay(in: contentRect, to: bitmapRep)
let image = NSImage(size: bitmapRep.size)
image.addRepresentation(bitmapRep)
return image
}
}

How to convert value of type 'ImagePickerView' to expected argument type 'String' on Swiftui?

Change #1:

Your model should usually be a struct unless there's a really compelling reason to make it an ObservableObject. In this case, struct works very well:

struct MovieAdd: Identifiable {
var id = UUID()
var movieName = ""
var isComplete : Bool = false
var movieImage : UIImage
}

Note that I've made movieImage a UIImage.

Change #2:

Use Image(uiImage:) in MovieRow. The MovieAdd property no longer needs @ObservableObject since it's just a struct.

Also notice that types in Swift should be capitalized to follow convention).

struct MovieRow: View {

var movieAdd : MovieAdd

var body: some View {
VStack {

Image(uiImage: movieAdd.movieImage)
.resizable()
.frame(width: 100, height: 100)

Text(movieAdd.movieName)
}
}
}

Complete code in case I forgot to mention any other changes:


struct ContentView: View {

@State var movieAdd: [MovieAdd] = []

@State private var newMovieName: String = ""
@State private var showNewMovie = false

@State private var newMovieImage = UIImage()

var body: some View {
ZStack {
VStack {
HStack {
Text("Movies Watched Ratings")
.font(.system(size: 40, weight: .black, design: .rounded
))

Spacer()

Button(action: {
self.showNewMovie = true
}) {
Image(systemName: "plus.circle.fill")
.font(.largeTitle)
.foregroundColor(.yellow)
}
}

List{
ForEach(movieAdd) {movie in
MovieRow(movieAdd: movie)
}
}
}

if showNewMovie {
BlankView(bGColor: .black)
.opacity(0.5)
.onTapGesture {
self.showNewMovie = false
}
NewMovieView(isShow: $showNewMovie, addMovie: $movieAdd, newMovieName: newMovieName)
.transition(.move(edge: .bottom))
.animation(.interpolatingSpring(stiffness: 200.0, damping: 25.0, initialVelocity: 10.0))
}
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

struct MovieRow: View {

var movieAdd : MovieAdd

var body: some View {
VStack {

Image(uiImage: movieAdd.movieImage)
.resizable()
.frame(width: 100, height: 100)

Text(movieAdd.movieName)
}
}
}

struct BlankView: View {
var bGColor: Color

var body: some View {
VStack {
Spacer()
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(bGColor)
.edgesIgnoringSafeArea(.all)
}
}

struct MovieAdd: Identifiable {
var id = UUID()
var movieName = ""
var isComplete : Bool = false
var movieImage : UIImage
}

struct NewMovieView: View {

@Binding var isShow: Bool
@Binding var addMovie: [MovieAdd]

@State var newMovieName: String = ""

@State var isShowingImagePicker = false
@State var imageInBlackBox = UIImage()

var body: some View {
ScrollView {
VStack {
VStack (alignment: .leading) {
HStack {
Text("Add a New Movie")
.font(.system(.title, design: .rounded))
.bold()
}
ZStack {
VStack {
HStack (alignment: .center){

Spacer()
Image(uiImage: imageInBlackBox)
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.border(Color.black, width: 3)
.clipped()
Spacer()
}

VStack {
Spacer()
Button(action: {
self.isShowingImagePicker.toggle()
}, label: {
Text("Select Image")
.font(.system(size: 15))
})
.sheet(isPresented: $isShowingImagePicker, content: { ImagePickerView(isPresented: $isShowingImagePicker, selectedImage: $imageInBlackBox)})
}
}
}

Group {
TextField("Enter the movie name", text: $newMovieName)
.padding()
.background(Color(.systemGray6))

}

Button(action: {
if self.newMovieName.trimmingCharacters(in: .whitespaces) == "" {
return
}

if self.isShowingImagePicker {
return
}

self.isShow = false
self.addMovieTask(movieName: self.newMovieName, movieImage: ImagePickerView(isPresented: $isShowingImagePicker, selectedImage: $imageInBlackBox))
}) {
Text("Save")
.font(.system(.headline, design: .rounded))
.foregroundColor(.red)
}
}
}
.background(Color.white)
}
}

private func addMovieTask(movieName: String, isComplete: Bool = false, movieImage: ImagePickerView) {

let task = MovieAdd(movieName: movieName, movieImage: movieImage.selectedImage)
addMovie.append(task)
}
}

struct NewMovieView_Previews: PreviewProvider {
static var previews: some View {
NewMovieView(isShow: .constant(true), addMovie: .constant([]), newMovieName: "", isShowingImagePicker: true)
}
}

struct ImagePickerView: UIViewControllerRepresentable {

@Binding var isPresented: Bool
@Binding var selectedImage: UIImage

func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePickerView>) -> some UIViewController {
let controller = UIImagePickerController()
controller.delegate = context.coordinator
return controller
}

func makeCoordinator() -> ImagePickerView.Coordinator {
return Coordinator(parent: self)
}

class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

let parent: ImagePickerView
init(parent: ImagePickerView){
self.parent = parent
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let selectedImage = info[.originalImage] as? UIImage {
print(selectedImage)
self.parent.selectedImage = selectedImage
}
self.parent.isPresented = false
}
}

func updateUIViewController(_ uiViewController: ImagePickerView.UIViewControllerType, context: UIViewControllerRepresentableContext<ImagePickerView>) {
//
}
}

SwiftUI - onDrag Argument type 'NSImage' does not conform to expected type 'NSItemProviderWriting'

The following works as Drag&Drop from testing SwiftUI app to TextEdit. Testing image image is stored in Assets.xcassets

Image("image")
.onDrag {
NSItemProvider(item: NSImage(named: "image")?.tiffRepresentation as NSSecureCoding?,
typeIdentifier: kUTTypeTIFF as String)
}

Convert UIImage to NSData and convert back to UIImage in Swift?

UIImage(data:imageData,scale:1.0) presuming the image's scale is 1.

In swift 4.2, use below code for get Data().

image.pngData()


Related Topics



Leave a reply



Submit