Generate QRimage upon button click - ios

I want to make the QRimage load after the button is clicked in swiftUI . Right now I just pass the UI Image generated by the function straight away into the image frame. How do I present the function in the button action section?
Right now My code looks like this;
struct QRCodeView: View {
#State private var UUID = "usufuf321"
#State private var phoneNo = "09-012948"
let context = CIContext()
let filter = CIFilter.qrCodeGenerator()
var body: some View {
GroupBox{
VStack(alignment: .center, spacing: 10){
Image(uiImage: QRGenerator(from: "\(UUID)\n\(phoneNo)"))
.interpolation(.none)
.resizable()
.scaledToFit()
.frame(width: 250, height: 250, alignment: .center)
.clipped()
Button(action: {
}, label: {
Text ("GENERATE")
.scaledToFit()
.frame(width: 250, height: 60, alignment: .center)
.cornerRadius(12)
})
}
.frame(maxWidth:.infinity, alignment: .center)
}
.cornerRadius(20)
.padding(.all,10)
}
func QRGenerator(from string: String) -> UIImage {
let data = Data(string.utf8)
filter.setValue(data, forKey: "inputMessage")
if let outputImage = filter.outputImage {
if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
return UIImage(cgImage: cgimg)
}
}
return UIImage(systemName: "xmark.circle") ?? UIImage()
}
}

Use #State
struct QRCodeView: View {
#State private var qrImage: Image?
var body: some View {
GroupBox{
VStack(alignment: .center, spacing: 10){
if let qrImage = qrImage {
qrImage
.interpolation(.none)
.resizable()
.scaledToFit()
.frame(width: 250, height: 250, alignment: .center)
.clipped()
}
Button(action: {
qrImage = Image(uiImage: QRGenerator(from: "\(UUID)\n\(phoneNo)"))
}, label: {
Text ("GENERATE")
.scaledToFit()
.frame(width: 250, height: 60, alignment: .center)
.cornerRadius(12)
})
}
.frame(maxWidth:.infinity, alignment: .center)
}
}
}

Related

SwiftUI AsyncImage orientation

I am using AsyncImage to download a jpg from my Parse Server. If I download the file manually it has an orientation of 6 (90 Degree counterclockwise), but when the file is returned by AsyncImage, it is missing this orientation and appears rotated.
As an aside, when I substitute SBPAsyncImage for AsyncImage, the orientation is correct.
Is there away for AsyncImage to detect and correct for orientation?
AsyncImage(url: photoURL) { image in
image
.resizable()
.scaledToFill()
.frame(width: 200, height: 200, alignment: .leading)
} placeholder: {
ProgressView()
}
Edit: I was over zealous in simplifying the code. Orientation is correct when AsyncImage is displayed in a simple View but my layout has a list of ScrollViews displaying the images fetched from a Parse Server. Here is a version of the original:
struct TimeLineView: View {
//: A view model in SwiftUI
#StateObject var viewModel = PFTour.query(matchesKeyInQuery(key: "routeID", queryKey: "uniqueID", query: PFRoute.query("state" == "Colorado")))
.order([.descending("date")])
.include("route")
.include("creator")
.include("photos")
.viewModel
var body: some View {
Group {
if let error = viewModel.error {
Text(error.description)
} else {
List(viewModel.results, id: \.id) { tour in
ParseTourImageOnlyScrollView(tour: tour)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
}
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.edgesIgnoringSafeArea(.all)
.onAppear(perform: {
viewModel.find()
})
}
Each cell displays a ScrollView:
struct ParseTourImageOnlyScrollView: View {
let tour: PFTour
var body: some View {
VStack {
Divider()
ScrollView(.horizontal) {
HStack(spacing: 5.0) {
if let photos = tour.photos {
ForEach(photos, id: \.self) { parsePhoto in
PhotoOnlyView(photoFile: parsePhoto.photo!)
}
}
}
}
Divider()
Spacer()
}
}
When comparing AsyncImage with BackPortAsyncImage, the former does not show the correct orientation for the image data at https://parsefiles.back4app.com/zgqRRfY6Tr5ICfdPocRLZG8EXK59vfS5dpDM5bqr/7d5eaf509e0745be0314aa493099dc82_file.bin:
struct PhotoOnlyView: View {
let photoFile: ParseFile
var body: some View {
if #available(iOS 15.0, *) {
VStack{
SwiftUI.AsyncImage(url: photoFile.url) { image in
image
.resizable()
.scaledToFill()
} placeholder: {
ProgressView()
}
.frame(width: UIScreen.main.bounds.size.width - 150, height: UIScreen.main.bounds.size.width - 150, alignment: .leading)
BackportAsyncImage(url: photoFile.url) { image in
image
.resizable()
.scaledToFill()
} placeholder: {
ProgressView()
}
.frame(width: UIScreen.main.bounds.size.width - 150, height: UIScreen.main.bounds.size.width - 150, alignment: .leading)
}
}
else {
ProgressView()
}
}

how to move to Detailview according to the data in ForEach SwiftUi?

So I've been trying to create a foreach containing an asyncimage which will later navigate to the detailview when pressed, but when I press the images that don't match the order of the images I have, my images show them in random order. I don't know how to go to detailview with asyncimage according to the order of data in foreach in HistoryView()
import SwiftUI
struct HistoryView: View {
#EnvironmentObject var historyManager : HistoryManager
#State var isActive : Bool = false
var body: some View {
NavigationView{
let jsonDataList = historyManager.jsondata
ScrollView{
VStack{
if let response = jsonDataList{
//MARK: diberi reversed agar ketika update maka akan munculnya dari bawah
ForEach(response.dataJson.result.check_in.reversed(), id:\.self){ items in
ZStack(alignment: .leading){
//color changing when late is true
if (historyManager.warna == true){
Image("RoundedRectangle-red")
.resizable()
.frame(width: 370, height: 200)
}else{
Image("RoundedRectangle-green")
.resizable()
.frame(width: 370, height: 200)
}
//Image
NavigationLink(isActive : $isActive){
DetailImageHistory(isActive: $isActive, name: items.links)
} label: {
let url = URL(string: items.links)
AsyncImage(url: url){ image in
image
.resizable()
.scaledToFill()
.frame(width: 45, height: 45)
.clipShape(Circle())
} placeholder: {
ProgressView()
.progressViewStyle(.circular)
}
.frame(width: 45, height: 45)
.clipShape(Circle())
}.offset(x: 300, y: -70)
//Content
VStack(alignment:.leading, spacing: 2){
Text("Presensi Datang").bold().font(.system(size: 15))
Text("\(items.createdAts)").font(.system(size: 15))
if(historyManager.keteranganKehadiran == true){
Text("Tepat Waktu").fontWeight(.bold).foregroundColor(.white)
}else{
Text("Terlambat").fontWeight(.bold).foregroundColor(.white).font(.system(size: 15))
}
Text("\(items.places)").font(.system(size: 15))
Text("\(items.locations)").multilineTextAlignment(.leading).font(.system(size: 15))
Text("Keterangan: ").bold().font(.system(size: 15))
Text("\(items.tujuans)").bold().font(.system(size: 15))
}.padding(.horizontal)
}
}
}
}
}
}
}
}
and this is my DetailView
import SwiftUI
struct DetailImageHistory: View {
#EnvironmentObject var historyManager: HistoryManager
#Binding var isActive : Bool
var name : String
var body: some View {
let url = URL(string: name)
AsyncImage(url: url){ image in
image
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 300, height: 300)
} placeholder: {
Color.gray
}
.frame(width: 300, height: 300)
}
}
UPDATE: my problem is solved i dont know why I'm not changing anything but somehow it is actually worked now lol

SwiftUI Crash BoxVTable

I am seeing an odd crash with some beta testers on TestFlight. It seems to be at app startup, but prior to the point where we are making any network calls. There does not seem to be a lot on the internet regarding this, and symbolication isn't helping much either. Just curious if anyone has run into this before and what they did about it? I have had it happen on two tester's devices and I have never been able to recreate the issue locally.
For a bit of context, I am creating a list view on my main screen with a LazyVGrid that contains a LazyHGrid inside of it that are both filled using published vars from my viewmodel, though this seems to happen prior to any of those being created.
Thanks for any ideas / help
Edit: Some more details / actual code:
The view with the grids:
import SwiftUI
import Kingfisher
struct FeaturedView: View {
#EnvironmentObject var viewRouter: ViewRouter
#EnvironmentObject var tabRouter : TabRouter
#StateObject var loadingViewModel = LoadingViewModel()
private let imageProcessor = SVGImgProcessor()
private let playerManager = PlayerManager.shared
private var gridItemLayout = [GridItem(.flexible())]
let userDefaults = UserDefaults.standard
var body: some View {
let padding: CGFloat = 20
let paddingHStack: CGFloat = 25
GeometryReader { geometry in
ZStack(alignment: .top){
Color(hex:"#00091C").edgesIgnoringSafeArea(.all)
VStack {
HStack {
HStack {
Text("Hello, \(loadingViewModel.name)")
.frame(alignment: .leading)
.multilineTextAlignment(.center)
.font(Font.custom("poppins-medium", size: 20))
.foregroundColor(Color(hex:"#667C95"))
.padding(.leading, 15)
.padding(.top, 15)
.padding(.bottom, 15)
Image("PremiumStar")
.resizable()
.frame(width: 15.0, height: 15.0)
.opacity(userDefaults.isSubscriber() ? 1 : 0)
}
Spacer()
Button(action: {
print("Settings Clicked")
viewRouter.currentPage = .settingsFlow
}) {
Image("Settings")
.resizable()
.frame(width: 22.0, height: 22.0)
.padding(15)
}
}
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
LazyVGrid(columns: gridItemLayout, spacing: 17) {
ForEach(loadingViewModel.getCategories()) { category in
Text(category.title)
.foregroundColor(.white)
.font(Font.custom("poppins-bold", size: 30))
ZStack {
KFImage(URL(string: (category.background?.svg!)!))
.resizable()
.setProcessor(imageProcessor)
.frame(width: geometry.size.width - padding, height: 175, alignment: .topLeading)
.aspectRatio(contentMode: .fill)
.clipped()
.cornerRadius(5)
ScrollView(.horizontal, showsIndicators: false) {
LazyHGrid(rows: gridItemLayout, spacing: 20){
ForEach(loadingViewModel.getSoundsForCategory(category: category.key)) { sound in
Button(action: {
playerManager.play(sound: sound)
}) {
VStack {
ZStack{
RoundedRectangle(cornerRadius: 5).frame(width: 90, height: 90)
.foregroundColor(Color.black)
.opacity(0.85)
ZStack(alignment:.bottomTrailing){
KFImage(URL(string: sound.icon.png!)!)
.resizable()
.renderingMode(.template)
.foregroundColor(.white)
.frame(width: 75, height: 75)
.aspectRatio(contentMode: .fill)
.clipped()
Image("LockIcon")
.frame(width: 12, height: 12, alignment: .bottomTrailing)
.aspectRatio(contentMode: .fill)
.clipped()
.hidden(loadingViewModel.isSubscriber || sound.tier != 2)
}
}
Text(sound.name)
.foregroundColor(.white)
.font(Font.custom("poppins-regular", size: 12))
.lineLimit(1)
.frame(minWidth: 0, idealWidth: 90, maxWidth: 100, alignment: .center)
}
}
}
}.padding(.horizontal)
}
.frame(width: geometry.size.width - paddingHStack, height: 175, alignment: .topLeading)
}
Button("Explore All"){
print("Explore All \(category.title) Tapped")
tabRouter.categoryKey = category.key
tabRouter.hasChanged = true
tabRouter.currentTab = 1
}
.font(Font.custom("poppins-bold", size: 15))
.foregroundColor(Color.white)
}
}.padding(.bottom, 120)
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
FeaturedView()
}
}
That is loaded by this tab view, which goes between two screens which share the same viewModel / load the same data
// MARK: Tab View
var body: some View {
ZStack{
TabView(selection: $selectedTab){
FeaturedView()
.environmentObject(tabRouter)
.tabItem{
Label("FEATURED", image: "Featured")
}.tag(0)
.overlay(
VStack {
if (showOverlay && !isExpanded){
playView
} else {
EmptyView()
}
}
)
SoundView()
.environmentObject(tabRouter)
.tabItem{
Label("SOUNDS", image: "Sounds")
}.tag(1)
.overlay(
VStack {
if (showOverlay && !isExpanded){
playView
} else {
EmptyView()
}
}
)
}
}
}

Issue convert SwiftUI View to an Image

I have a goal to convert View to Image using SwiftUI.
From the first image I have a look that is exactly what I want.
Link picture View will be convert to an Image:
but when I convert the display to an image, the background object and the sticker (Santa's hat) size and position are messed up, even though I've made it neat and fit exactly what I want.
the following is the result of the image:
View results that have been converted into an Image:
This is my Code View
import SwiftUI
struct SnapshotView: View {
#ObservedObject var effectData: EffectViewModel
#ObservedObject var eraseModel: EraseViewModel
#ObservedObject var stickerData: StickerViewModel
#Binding var image: UIImage?
#Binding var isActive: Bool
#Binding var isEffectAvailable: Bool
#Binding var sliderValueBlur: Double
init(image: Binding<UIImage?>, isActive: Binding<Bool>,
isEffectAvailable: Binding<Bool>,
sliderValueBlur: Binding<Double>,
effectData: ObservedObject<EffectViewModel>,
eraseModel: ObservedObject<EraseViewModel>,
stickerData: ObservedObject<StickerViewModel>) {
_image = image
_isActive = isActive
_isEffectAvailable = isEffectAvailable
_sliderValueBlur = sliderValueBlur
_effectData = effectData
_eraseModel = eraseModel
_stickerData = stickerData
}
var body: some View {
GeometryReader { geo in
VStack {
ZStack(alignment: .center) {
if isEffectAvailable {
VStack {
Image(uiImage: self.effectData.modelEffect!.image_effect)
.resizable()
.scaledToFit()
.frame(width: geo.size.width, alignment: .center)
.position(self.effectData.modelEffect!.position_effect)
.scaleEffect(self.effectData.modelEffect!.currentScale)
.mask(
Image(uiImage: image!)
.resizable()
.scaledToFit()
.frame(width: geo.size.width, alignment: .center)
)
.blur(radius: CGFloat(self.effectData.modelEffect!.valueBlur))
.opacity(self.effectData.modelEffect!.opacity_effect)
}
.background(
ZStack {
Image(uiImage: image!)
.resizable()
.scaledToFit()
.frame(width: geo.size.width, alignment: .top)
.blur(radius: CGFloat(sliderValueBlur)*4)
}
.border(Color.black, width: 1)
)
} else {
ZStack {
Image(uiImage: image!)
.resizable()
.scaledToFit()
.frame(width: geo.size.width, alignment: .top)
.blur(radius: CGFloat(sliderValueBlur)*4)
}
.border(Color.black, width: 1)
}
Image(uiImage: self.eraseModel.inputImage)
.resizable()
.scaledToFit()
.frame(width: geo.size.width, alignment: .center)
.overlay(
VStack {
ForEach(stickerData.selectedSticker.indices, id: \.self) { index in
ZStack(alignment: .bottomTrailing) {
Image(uiImage: stickerData.selectedSticker[index].image)
.resizable()
.frame(width: stickerData.selectedSticker[index].width, height: stickerData.selectedSticker[index].height, alignment: .center)
.scaleEffect(stickerData.selectedSticker[index].scale)
.overlay(
Rectangle()
.stroke(Color.gray, lineWidth: stickerData.selectedSticker[index].isEditing ? 4 : 0)
)
}
.frame(width: 130, height: 130, alignment: .center)
.position(stickerData.selectedSticker[index].position)
.scaleEffect(stickerData.selectedSticker[index].scale)
}
}
)
}
}
}
}
}
And this is the function I use to convert View to Image
extension UIView {
var renderedImage: UIImage {
// rect of capure
let rect = self.bounds
// create the context of bitmap
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
let context: CGContext = UIGraphicsGetCurrentContext()!
self.layer.render(in: context)
// get a image from current context bitmap
let capturedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return capturedImage
}
}
extension View {
func takeScreenshot(origin: CGPoint, size: CGSize) -> UIImage {
let window = UIWindow(frame: CGRect(origin: origin, size: size))
let hosting = UIHostingController(rootView: self)
hosting.view.frame = window.frame
window.addSubview(hosting.view)
window.makeKeyAndVisible()
return hosting.view.renderedImage
}
}
Usage example:
Button(action: {
let viewSnapshot = SnapshotView(image: self.$image, isActive: self.$isActive, isEffectAvailable: self.$isEffectAvailable, sliderValueBlur: self.$sliderValueBlur, effectData: self._effectData, eraseModel: self._eraseModel, stickerData: self._stickerData)
let saveImage = viewSnapshot.takeScreenshot(origin: geo.frame(in: .global).origin, size: self.image!.size)
UIImageWriteToSavedPhotosAlbum(saveImage, nil, nil, nil)
}, label: {
Text("SAVE")
.foregroundColor(.white)
})
Maybe you can help me, what's wrong here

SwiftUI - Button with Image is clickable outside

I have a ScrollView with multiple Buttons. A Button contains a Image and a Text underneath.
As the images are pretty large I am using .scaledToFill and .clipped. And it seems that the 'clipped' part of the image is still clickable even if it's not shown.
In the video you see I am clicking on button 1 but button 2 is triggered.
This is my Coding. The Image is inside the View Card.
struct ContentView: View {
#State var useWebImage = false
#State var isSheetShowing = false
#State var selectedIndex = 0
private let images = [
"https://images.unsplash.com/photo-1478368499690-1316c519df07?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2706&q=80",
"https://images.unsplash.com/photo-1507154258-c81e5cca5931?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2600&q=80",
"https://images.unsplash.com/photo-1513310719763-d43889d6fc95?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80",
"https://images.unsplash.com/photo-1585766765962-28aa4c7d719c?ixlib=rb-1.2.1&auto=format&fit=crop&w=2734&q=80",
"https://images.unsplash.com/photo-1485970671356-ff9156bd4a98?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80",
"https://images.unsplash.com/photo-1585607666104-4d5b201d6d8c?ixlib=rb-1.2.1&auto=format&fit=crop&w=2700&q=80",
"https://images.unsplash.com/photo-1577702066866-6c8897d06443?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2177&q=80",
"https://images.unsplash.com/photo-1513809491260-0e192158ae44?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2736&q=80",
"https://images.unsplash.com/photo-1582092723055-ad941d1db0d4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2700&q=80",
"https://images.unsplash.com/photo-1478264635837-66efba4b74ba?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjF9&auto=format&fit=crop&w=2682&q=80"
]
var body: some View {
NavigationView {
ScrollView {
VStack(spacing: 40) {
Text(useWebImage ? "WebImage is used." : "SwiftUI Image is used")
.font(.system(size: 18))
.bold()
.kerning(0.5)
.padding(.top, 20)
Toggle(isOn: $useWebImage) {
Text("Use WebImage")
.font(.system(size: 18))
.bold()
.kerning(0.5)
.padding(.top, 20)
}
ForEach(0..<images.count) { index in
Button(action: {
self.selectedIndex = index
self.isSheetShowing.toggle()
}) {
Card(imageUrl: self.images[index], index: index, useWebImage: self.$useWebImage)
}
.buttonStyle(PlainButtonStyle())
}
}
.padding(.horizontal, 20)
.sheet(isPresented: self.$isSheetShowing) {
DestinationView(imageUrl: self.images[self.selectedIndex], index: self.selectedIndex, useWebImage: self.$useWebImage)
}
}
.navigationBarTitle("Images")
}
}
}
struct Card: View {
let imageUrl: String
let index: Int
#Binding var useWebImage: Bool
var body: some View {
VStack {
if useWebImage {
WebImage(url: URL(string: imageUrl))
.resizable()
.indicator(.activity)
.animation(.easeInOut(duration: 0.5))
.transition(.fade)
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
.cornerRadius(12)
.clipped()
} else {
Image("image\(index)")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
.cornerRadius(12)
.clipped()
}
HStack {
Text("Image #\(index + 1) (\(useWebImage ? "WebImage" : "SwiftUI Image"))")
.font(.system(size: 18))
.bold()
.kerning(0.5)
Spacer()
}
}
.padding(2)
.border(Color(.systemRed), width: 2)
}
}
Do you have an idea how to fix this issue?
I already tried to use .resizable(resizingMode: .tile) but I need to shrink the image before I could use just a tile.
For detailed information you can also find the project on GitHub GitHub Project
I would appreciate your help a lot.
The .clipped affects only drawing, and by-default Button has all content clickable not depending what it is.
So if you want make your button clickable only in image area, you have to limit hit testing only to its rect explicitly and disable everything else.
Here is a demo of possible approach. Tested with Xcode 11.4 / iOS 13.4.
Demo code (simplified variant of your snapshot):
struct ButtonCard: View {
var body: some View {
VStack {
Image("sea")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
.cornerRadius(12)
.contentShape(Rectangle()) // << define clickable rect !!
.clipped()
HStack {
Text("Image #1")
.font(.system(size: 18))
.bold()
.kerning(0.5)
Spacer()
}.allowsHitTesting(false) // << disable label area !!
}
.padding(2)
.border(Color(.systemRed), width: 2)
}
}
struct TestClippedButton: View {
var body: some View {
Button(action: { print(">> tapped") }) {
ButtonCard()
}.buttonStyle(PlainButtonStyle())
}
}

Resources