Coredata Image conversion swiftUI - image

I'm building a simple CoreData app. At one point the user is able to upload and store an image with CoreData in the NSData format. Saving the managedobjectcontext works like follows:
let item = Item(context: self.managedObjectContext)
item.theImage = selectedImageFromPicker.pngData() as NSData?
//saving the MOC
Now, when im trying to retrieve the image im facing a series of issues.
struct Box {
var id: Int
let title: String
let image: NSData?
}
struct BoxView: View {
let box: Box
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var item: Item
var body: some View {
VStack {
Image(uiImage: UIImage(data: box.image) as! Data)
.resizable()
.frame(width: 120, height: 120, alignment: .center)
.aspectRatio(contentMode: .fill)
}
}
}
I'm pretty sure displaying the Image() with a UIImage that contains data is faulty here, but I cant figure out how to convert the data to a format that is displayed by the Image() method.
Unfortunately Xcode won't help with its error messages either and rather blames it on the VStack.
Maybe someone else was in the situation before and could help because I didn't find anything online to this specific issue.

As far as I understood you need something like the following (I removed non-related to image code for shortness and simplicity)
var body: some View {
VStack {
image
}
}
var image: some View {
if let nsData = box.image, let uiImage = UIImage(data: nsData as Data) {
return AnyView(Image(uiImage: uiImage)
.resizable()
.frame(width: 120, height: 120, alignment: .center)
.aspectRatio(contentMode: .fill))
}
return AnyView(EmptyView())
}

Here's how I did it. It works with a list.
I put this function in the struct view...
func getImageFromData(show: MyShow) -> UIImage {
//this is just a placeholder
var finalImage = (UIImage(systemName: "xmark"))
if let data = show.image {
if let image = UIImage(data: data as Data) {
finalImage = image
}
}
return finalImage!
}
and then I put this in the body view...
var body: some View {
NavigationView {
List {
ForEach(myShows, id: \.self) { (show: MyShow) in
VStack {
Image(uiImage: self.getImageFromData(show: show))
}
}
}
}

Related

animation 'has been deprecated in iOS 15.0: use instead with Animation or animation (_: value :)

I apply ".animation (.easeIn)" but it is deprecated.
tells me: animation 'has been deprecated in iOS 15.0: use instead with Animation or animation (: value :), but animation (: value :) I don't know which value I need to pass. I can't create animations that start slowly. Can you please help me understand how I can properly use animation (_: value :) to make animation work? I have to create the animation that when I move the slider, the images slowly recompose in space according to how much they are enlarged or reduced.
thank you.
'''
import SwiftUI
struct GalleryView: View {
// MARK: - PROPERTIES
#State private var selectedAnimal: String = "lion"
let animals: [Animal] = Bundle.main.decode("animals.json")
let haptics = UIImpactFeedbackGenerator(style: .medium)
#State private var gridLayout: [GridItem] = [GridItem(.flexible())]
#State private var gridColumn: Double = 3.0
func gridSwitch() {
gridLayout = Array(repeating: .init(.flexible()), count: Int(gridColumn))
}
// MARK: - BODY
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .center, spacing: 30) {
// MARK: - IMAGE
Image(selectedAnimal)
.resizable()
.scaledToFit()
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 8))
// MARK: - SLIDER
Slider(value: $gridColumn, in: 2...4, step: 1) // se aumento il valore nello slider ci saranno piĆ¹ sezioni
.padding(.horizontal)
.onChange(of: gridColumn, perform: { value in
gridSwitch()
})
// MARK: - GRID
LazyVGrid(columns: gridLayout, alignment: .center, spacing: 10) {
ForEach(animals) { item in
Image(item.image)
.resizable()
.scaledToFit()
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 1))
.onTapGesture {
selectedAnimal = item.image
haptics.impactOccurred()
}
} //: LOOP
} //: GRID
.animation(.easeIn)
.onAppear(perform: {
gridSwitch()
})
} //: VSTACK
.padding(.horizontal, 10)
.padding(.vertical,50)
} //: SCROLL
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(MotionAnimationView())
}
}
// MARK: - PREVIEW
struct GalleryView_Previews: PreviewProvider {
static var previews: some View {
GalleryView()
}
}
As the error says, .animation() is deprecated, with iOS 15, you need to use .animation(value:) now. where the value is the binding that you want your animation to be triggered, in your case, I assume it is selectedAnimal.
Applying this would be something like
VStack {
}
.animation(.easeIn, value: selectedAnimal)
As discussed in comments, if you want your gridLayout to be animatable, it is a little bit more tricky. Because Arrays has to be Equatable if you want them to be animatable, since extending the GridItem is not a good solution, I came up with this:
delete your .animation method change your gridSwitch function with this:
struct GalleryView: View {
// ... irrelevant code
#State private var isGridChanged = false
func gridSwitch() {
if (isGridChanged) {
withAnimation {
gridLayout = Array(repeating: .init(.flexible()), count: Int(gridColumn))
}
}
else {
gridLayout = Array(repeating: .init(.flexible()), count: Int(gridColumn))
isGridChanged = true
}
}
isGridChanged is required because as you're changing your gridLayout when your View is initialized, it causes a weird bug that everything is getting scaled down when app launches because of withAnimation.

SwiftUI - How to make images from URLs with different sizes fit the frame?

I'm trying to have a series of favicons downloaded from the internet, and then inserted in a squared-shaped frame. However, they have different sizes, and nor with scaledToFit() or scaledToFill() they seem to fix the issue.
Here's the code:
import SwiftUI
struct ContentView: View {
var websites = ["https://www.reddit.com/favicon.ico", "https://www.facebook.com/favicon.ico", "https://www.instagram.com/favicon.ico", "https://www.google.com/favicon.ico", "https://www.netflix.com/favicon.ico", "https://www.amazon.com/favicon.ico"]
var body: some View {
NavigationView {
Form {
ForEach(websites, id: \.self) { wbs in
Image(uiImage: try! UIImage(withContentsOfUrl: URL(string: wbs)!)!)
.frame(width: 40, height: 40, alignment: .center)
.scaledToFit()
.clipShape(RoundedRectangle(cornerRadius: 10))
.overlay(RoundedRectangle(cornerRadius: 10)
.strokeBorder(Color.gray, lineWidth: 1.0)
.foregroundColor(.clear))
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension UIImage {
convenience init?(withContentsOfUrl url: URL) throws {
let imageData = try Data(contentsOf: url)
self.init(data: imageData)
}
}
What I'd like to achieve is that all icons fill the frame the same way. For example, in the screenshot I attached, the favicons of Reddit and Instagram look good, while Facebook is too small and Amazon or Netflix are too big.
Thanks to everyone!
Add the
.resizable()
Modifier to Image

ios widget load remote image url in TimelineProvider?

I'm showing a stock chart (eg: https://example.com/my_img.png) on my widget.
As investigation on SO & Apple dev 4rum, I cannot found a workable solution.
As I know, Widget does not support async image loading so my approach is fetching the image at the time we getTimeline(), then pass the image data into Entry to show it later.
But I cannot make it work, can anyone point me out how to resolve it?
In short, it's not working!:
// Fetch first in getTimeline()
let imgData = Data(
contentsOf: URL(string: "https://example.com/my_img.png")!
)!
// Then render it later in View
Image(uiImage: UIImage(data: imgData))
Here is all my pseudo code:
func getTimeline(...) {
var imageData = nil
let url: URL = URL(string: "https://example.com/my_img.png")!
if let imgData:Data = try? Data(contentsOf: url) {
imageData = imgData
}
var entry = SimpleEntry(...)
entry.imageData = imageData
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
// Some where in the widget i'll show the imageData above
struct FIM_SV_Chart: View {
let imageData: Data?
var body: some View {
if imageData != nil, let uiImage = UIImage(data: imageData!) {
Image(uiImage: uiImage)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 26.0)
} else {
Image("EmptyChart")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 26.0)
}
}
}
Many thanks.
It's my fault, I generated an incorrect image url so imgData is 0 byte!
This will work:
// Fetch first in getTimeline()
let imgData = Data(
contentsOf: URL(string: "https://example.com/my_img.png")!
)!
// Then render it later in View
Image(uiImage: UIImage(data: imgData))

How do I render a SwiftUI View that is not at the root hierarchy as a UIImage?

Suppose I have a simple SwiftUI View that is not the ContentView such as this:
struct Test: View {
var body: some View {
VStack {
Text("Test 1")
Text("Test 2")
}
}
}
How can I render this view as a UIImage?
I've looked into solutions such as :
extension UIView {
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
But it seems that solutions like that only work on UIView, not a SwiftUI View.
Here is the approach that works for me, as I needed to get image exactly sized as it is when placed alongside others. Hope it would be helpful for some else.
Demo: above divider is SwiftUI rendered, below is image (in border to show size)
Update: re-tested with Xcode 13.4 / iOS 15.5
Test module in project is here
extension View {
func asImage() -> UIImage {
let controller = UIHostingController(rootView: self)
// locate far out of screen
controller.view.frame = CGRect(x: 0, y: CGFloat(Int.max), width: 1, height: 1)
let size = controller.sizeThatFits(in: UIScreen.main.bounds.size)
controller.view.bounds = CGRect(origin: .zero, size: size)
controller.view.sizeToFit()
UIApplication.shared.windows.first?.rootViewController?.view.addSubview(controller.view)
let image = controller.view.asImage()
controller.view.removeFromSuperview()
return image
}
}
extension UIView {
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
// [!!] Uncomment to clip resulting image
// rendererContext.cgContext.addPath(
// UIBezierPath(roundedRect: bounds, cornerRadius: 20).cgPath)
// rendererContext.cgContext.clip()
// As commented by #MaxIsom below in some cases might be needed
// to make this asynchronously, so uncomment below DispatchQueue
// if you'd same met crash
// DispatchQueue.main.async {
layer.render(in: rendererContext.cgContext)
// }
}
}
}
// TESTING
struct TestableView: View {
var body: some View {
VStack {
Text("Test 1")
Text("Test 2")
}
}
}
struct TestBackgroundRendering: View {
var body: some View {
VStack {
TestableView()
Divider()
Image(uiImage: render())
.border(Color.black)
}
}
private func render() -> UIImage {
TestableView().asImage()
}
}
Solution of Asperi works, but if you need image without white background you have to add this line:
controller.view.backgroundColor = .clear
And your View extension will be:
extension View {
func asImage() -> UIImage {
let controller = UIHostingController(rootView: self)
// locate far out of screen
controller.view.frame = CGRect(x: 0, y: CGFloat(Int.max), width: 1, height: 1)
UIApplication.shared.windows.first!.rootViewController?.view.addSubview(controller.view)
let size = controller.sizeThatFits(in: UIScreen.main.bounds.size)
controller.view.bounds = CGRect(origin: .zero, size: size)
controller.view.sizeToFit()
controller.view.backgroundColor = .clear
let image = controller.view.asImage()
controller.view.removeFromSuperview()
return image
}
}

Add Image to TextField/SecureField in SwiftUI, add padding to placeholder text

I made a textfield and a securetextfield in SwiftUI but I have no idea how to add in an image into my textfield/secure textfield in SwiftUI. There is not much documentation online for SwiftUI like there was for the older versions of Swift. I also want to shift over the (placeholder/typed in text) over by a designated amount say for example like 30 points to the right. I also was trying out to see if the background color would change from white to red, but as you can see, it is in my code with no effect on the UI.
Note:I have the GeometryReader called earlier in my code as well as the #state variables for the username and the password.
My goal is to have it look like this , right now it looks like this
VStack (spacing: deviceSize.size.height * (50/812)) {
TextField ("Username", text: self.$username)
.foregroundColor(.black)//text color when you type
.accentColor(.blue)//cursor color
.background(Color(.red))//????
.textFieldStyle(RoundedBorderTextFieldStyle())
.cornerRadius(50)
// .border(Color.white)
//.font(.title)
SecureField ("Password", text: self.$password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.cornerRadius(50)
}
.padding(.init(top: 0, leading: deviceSize.size.width * (38/375), bottom: 0, trailing: deviceSize.size.width * (38/375)))
The easiest way to achieve such a design would be to place the Image and TextField in a HStack and give it one Rounded background. It is slightly more complicated with the password field as it needs an extra Button, and when you hide/show the password you need to change between TextField and SecureField. Here is my take on it:
struct ContentView: View {
#State private var username = ""
#State private var password = ""
#State private var showPassword = false
var body: some View {
ZStack {
Color.blue
VStack {
HStack {
Image(systemName: "person")
.foregroundColor(.secondary)
TextField("Username",
text: $username)
} .padding()
.background(Capsule().fill(Color.white))
HStack {
Image(systemName: "lock")
.foregroundColor(.secondary)
if showPassword {
TextField("Password",
text: $password)
} else {
SecureField("Password",
text: $password)
}
Button(action: { self.showPassword.toggle()}) {
Image(systemName: "eye")
.foregroundColor(.secondary)
}
} .padding()
.background(Capsule().fill(Color.white))
} .padding()
}
}
}
I'm really new to SwiftUI, but I found a workaround for this that I hope doesn't cause any issues in the future or it will be a big lesson learned. If anyone has any suggestion I'd appreciate it too! =]
I embedded the TextField and the image in a ZStack and I put the image inside a View and gave the view a padding.
struct FormInputBox: View {
#State private var text: String = ""
#State private var textFieldState: TextFieldState = .empty
private var textFieldType: TextFieldType
private var textViewPlaceholder = ""
init(placeholder: String,
textFieldType: TextFieldType) {
self.textViewPlaceholder = placeholder
self.textFieldType = textFieldType
}
var body: some View {
ZStack(alignment: Alignment(horizontal: .trailing, vertical: .center), content: {
TextField(textViewPlaceholder, text: $text)
.textFieldStyle(MyTextFieldStyle(textFieldState: $textFieldState))
AnyView(
Image("tick")
.resizable()
.frame(width: 20, height: 20, alignment: .leading)
)
.padding(32)
})
}
I have created a reusable SwiftUI Textfield named ASTextField which works similar to the textField in UIKit, where you can add the leftView and rightView of the textField and can handle the events related them.
You can find the implementation of this at gist.
This the way you can consume it:-
struct ContentView : View , ASTextFieldDelegate {
let leftImage = UIImage(systemName: "calendar")
let rightImage = UIImage(systemName: "eye")
let rightImage1 = UIImage(systemName: "trash")
#State var text : String? = "with simple binding"
#State var text1 : String? = "with closure for right item"
#State var text2 : String? = "for secure entry"
var body: some View {
VStack {
Spacer()
ASTextField(text: $text)
Spacer()
ASTextField(rightItem: rightImage1, leftItem: leftImage, handleLeftTap: {
print("right icon tapped.....")
}, delegate: self, text: $text1)
Spacer()
ASTextField(rightItem: rightImage, leftItem: leftImage, isSecuredEntry: true, delegate: self, text: $text2)
Spacer()
}
}
}
"Introspect" will work for you
Textfield()
.introspectTextField { textfield in
textfield.rightViewMode = .unlessEditing
textfield.rightView = UIImageView(image: UIImage(named: ImageCatalog.error.content))
}
I am totally newborn toddle in iOS Dev. So i wrote just like this. My apologises in advance if someone will get blind from the ugliness of the written code.
struct ContentView: View {
#State private var nameSearch: String = ""
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 25)
.frame(width: 230, height: 30)
.border(.black, width: 0.2)
.foregroundColor(.white)
HStack {
ZStack {
Image(systemName: "magnifyingglass.circle")
.foregroundColor(.gray)
.frame(width: 10, height: 10, alignment: .leading)
.padding(.trailing, 200)
TextField( "Search", text: $nameSearch)
.frame(width: 180, height: 30)
.padding(.leading, 20 )
}
}
}

Resources