Image not appearing when using .transition - swift

struct BEHOLD: View {
#State private var showdeatail = false
var body: some View {
VStack {
Button(action: {
showdeatail.toggle()
}) {
Text("S U M M O N\nH I M")
}
.frame(width: 100, height: 50)
.background(Color.red)
.cornerRadius(10)
if showdeatail {
Image("HIM")
.frame(width: 100, height: 100)
.transition(.scale)
}
}
}
So I'm trying to make this image appear when you hit a button and the code seems to be working except the Image seems to be invisible and I'm not sure why

Try this code, it would work!
struct BEHOLD: View {
#State private var showdeatail = false
var body: some View {
VStack {
Button(action: {
showdeatail.toggle()
}) {
Text("S U M M O N\nH I M")
}
.background(Color.red)
.cornerRadius(10)
if showdeatail {
Image(systemName: "person")
.resizable()
.frame(width: 100, height: 100)
.transition(.scale)
}
}
.animation(.default, value: showdeatail)
}
}

Related

SwiftUI How to Disable Button While Uploading Objects in an Array

I have a SwiftUI form where the user adds images to an array from a picker and then a function fires to upload each image. The images are displayed in a ForEach as either an upload spinner while the upload is happening OR the actual image once the upload has completed.
I'd like the NEXT button, which would remove the user from the view, to be disabled until all of the uploads have completed.
I'm not sure how to inform the State of the Parent View that all of the uploads are completed.
Here's what my Parent View looks like:
struct ProjectFormStep4: View {
#EnvironmentObject var authVM: AuthorizationClass
#EnvironmentObject var projectVM: ProjectsViewModel
#EnvironmentObject var uploadVM: UploadManager
#ObservedObject var mediaItems = PickedMediaItems()
#State private var showSheet: Bool = false
#State private var showUploadView: Bool = false
var body: some View {
ZStack{
Color("BrandGrey").ignoresSafeArea()
VStack{
HStack{
Button{
projectVM.showProjectFormStep1 = false
projectVM.showProjectFormStep2 = false
projectVM.showProjectFormStep3 = true
projectVM.showProjectFormStep4 = false
} label: {
Text("< Back")
.font(.headline)
.foregroundColor(Color("BrandLightBlue"))
}
Spacer()
}
Spacer()
.frame(height: 30)
ZStack{
Rectangle()
.fill(Color(.white))
.frame(width: 300, height: 100)
.cornerRadius(12)
Image(systemName: "camera")
.resizable()
.foregroundColor(Color("BrandLightBlue"))
.scaledToFit()
.frame(height: 60)
}.onTapGesture {
self.showSheet = true
}
Spacer()
.sheet(isPresented: $showSheet, content: {
PhotoPicker(mediaItems: mediaItems) { didSelectItem in
showUploadView = true
showSheet = false
}
})
Spacer()
ScrollView{
ForEach(uploadVM.uploadedMedia, id:\.id){ item in
ImageArea(
item: item,
items: uploadVM.uploadedMedia
)
}
}
Spacer()
if showUploadView {
ForEach(mediaItems.items, id: \.id) { item in
UploadView(item: item)
}
}
Button {
} label: {
Text("Next")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(width: 220, height: 60)
.background(Color("BrandOrange"))
.cornerRadius(15.0)
}
}.padding()
}
}
}
Here's my Child View of which handles the upload spinner and actual image:
struct ImageArea: View {
#EnvironmentObject var projectVM: ProjectsViewModel
#EnvironmentObject var uploadVM: UploadManager
#State private var showThumbnailOptions: Bool = false
var item: UploadedMedia
var items : [UploadedMedia]
var body: some View {
if item.secureUrl == "" {
ZStack{
UploadSpinner()
.frame(
width: 300,
height: 200
)
.cornerRadius(12)
VStack(alignment: .leading ){
HStack{
Spacer()
.frame(
width: 30
)
Image(systemName: self.getWaterMarkName(item: item))
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 24, height: 24)
.padding(4)
.background(Color.black.opacity(0.5))
.foregroundColor(.white)
}
Spacer()
HStack{
Spacer()
Text("\(item.uploadProgress)%")
.foregroundColor(.black)
.font(.body)
.padding()
Spacer()
}
}
}
} else {
ZStack{
ProjectRemoteUploadImage(projectImageUrl: projectVM.standardizeThumbnail(thumbnailUrl: item.secureUrl))
.aspectRatio(contentMode: .fit)
.onTapGesture {
showThumbnailOptions.toggle()
}
if showThumbnailOptions {
Color(.black).opacity(0.8)
VStack{
Spacer()
HStack{
Spacer()
Image(systemName: "arrowshape.turn.up.backward.circle.fill")
.resizable()
.foregroundColor(.white)
.frame(width: 50, height: 50)
.padding()
.onTapGesture {
self.showThumbnailOptions.toggle()
}
Image(systemName: "trash.circle.fill")
.resizable()
.foregroundColor(.red)
.frame(width: 50, height: 50)
.padding()
.onTapGesture {
deleteThumbnail(
item: item
)
}
Spacer()
}
Spacer()
}
}
}
}
}
func getWaterMarkName(item: UploadedMedia) -> String {
if item.mediaType == "photo"{
return "photo"
} else if item.mediaType == "video" {
return "video"
} else {
return "photo"
}
}
func deleteThumbnail(item: UploadedMedia){
let itemID = item.id
guard let itemIndex = uploadVM.uploadedMedia.firstIndex(where: {$0.id == itemID}) else { return }
uploadVM.uploadedMedia.remove(at: itemIndex)
}
}
Adding model info for the uploadedMedia
struct UploadedMedia {
var id: String
var uploadProgress: Float
var secureUrl: String
var mediaType: String
var width: Int
var length: Double
init(
id: String,
uploadProgress: Float,
secureUrl: String,
mediaType: String,
width: Int,
length: Double
){
self.id = id
self.uploadProgress = uploadProgress
self.secureUrl = secureUrl
self.mediaType = mediaType
self.width = width
self.length = length
}
}

Animating a flashing bell in SwiftUI

I am having problems with making a simple systemIcon flash in SwiftUI.
I got the animation working, but it has a silly behaviour if the layout of
a LazyGridView changes or adapts. Below is a video of its erroneous behaviour.
The flashing bell stays in place but when the layout rearranges the bell
starts transitioning in from the bottom of the parent view thats not there anymore.
Has someone got a suggestion how to get around this?
Here is a working example which is similar to my problem
import SwiftUI
struct FlashingBellLazyVGrid: View {
#State var isAnimating = false
#State var showChart = true
var body: some View {
let columns = [GridItem(.adaptive(minimum: 300), spacing: 50, alignment: .center)]
VStack {
Button(action: {
showChart.toggle()
}) {
VStack {
Circle()
.fill(showChart ? Color.green : Color.red)
.shadow(color: Color.gray, radius: 5, x: 2, y: 2)
Text("Charts")
.foregroundColor(Color.primary)
}.frame(width: 150, height: 50)
}
ScrollView {
LazyVGrid (
columns: columns, spacing: 50
) {
ForEach(0 ..< 25) { item in
ZStack {
Rectangle()
.fill(Color.red)
.cornerRadius(15)
VStack {
HStack {
Text(/*#START_MENU_TOKEN#*/"Hello, World!"/*#END_MENU_TOKEN#*/)
Spacer()
Image(systemName: "bell.fill")
.foregroundColor(Color.yellow)
.opacity(self.isAnimating ? 1 : 0)
.animation(Animation.easeInOut(duration: 0.66).repeatForever(autoreverses: false))
.onAppear{ self.isAnimating = true }
}.padding(50)
if showChart {
Rectangle()
.fill(Color.green)
.frame(height: 200)
}
}
}
}
}
}
}
}
}
struct FlashingBellLazyVGrid_Previews: PreviewProvider {
static var previews: some View {
FlashingBellLazyVGrid()
}
}
how it looks like before you click the showChart button at the top
After you toggle the button it looks like the bells are erroneously moving into place from the bottom of the screen. and toggling it back to its original state doesn't resolve this bug subsequently.
[
Looks like the animation is basing itself off of the original size of the view. In order to trick it into recognizing the new view size, I used .id(UUID()) on the outside of the grid. In a real world application, you'd probably want to be careful to store this ID somewhere and only refresh it when needed -- not on every re-render like I'm doing:
struct FlashingBellLazyVGrid: View {
#State var showChart = true
let columns = [GridItem(.adaptive(minimum: 300), spacing: 50, alignment: .center)]
var body: some View {
VStack {
Button(action: {
showChart.toggle()
}) {
VStack {
Circle()
.fill(showChart ? Color.green : Color.red)
.shadow(color: Color.gray, radius: 5, x: 2, y: 2)
Text("Charts")
.foregroundColor(Color.primary)
}.frame(width: 150, height: 50)
}
ScrollView {
LazyVGrid (
columns: columns, spacing: 50
) {
ForEach(0 ..< 25) { item in
ZStack {
Rectangle()
.fill(Color.red)
.cornerRadius(15)
VStack {
SeparateComponent()
if showChart {
Rectangle()
.fill(Color.green)
.frame(height: 200)
}
}
}
}
}
.id(UUID()) //<-- Here
}
}
}
}
struct SeparateComponent : View {
#State var isAnimating : Bool = false
var body: some View {
HStack {
Text("Hello, World!")
Spacer()
Image(systemName: "bell.fill")
.foregroundColor(Color.yellow)
.opacity(self.isAnimating ? 1 : 0)
.animation(Animation.easeInOut(duration: 0.66).repeatForever(autoreverses: false))
.onAppear{
self.isAnimating = true
}
}
.padding(50)
}
}
I also separated out the blinking component into its own view, since there were already problematic things happening with the existing logic with onAppear, which wouldn't affect newly-scrolled-to items correctly. This may need refactoring for your particular case as well, but this should get you started.

SwiftUI - how to increase Toggle's target touch size?

I want to increase Toggle's target size for usability purposes.
It's easy with buttons using .frame(width: 200, height: 200) . But I don't know how to apply the same for toggles.
Here is my code. I have marked where it works and where it doesn't.
import SwiftUI
struct ContentView: View {
#State private var test = true
var body: some View {
VStack {
Text("Hello, world!")
.padding()
Button(action: {
print("button pressed")
}) {
Image(systemName: "checkmark.circle.fill")
.font(.largeTitle)
// This works for Button
.frame(width: 200, height: 200)
}
Toggle(isOn: Binding<Bool>(
get: { test },
set: {
test = $0
})) {
Text("Done")
}
// This doesn't work for Toggle
.frame(width: 200, height: 200)
.labelsHidden()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You can try something like this:
Toggle(isOn: Binding<Bool>(
get: { test },
set: {
test = $0
})) {
Text("Done")
}
.frame(width: 200, height: 200)
.labelsHidden()
.contentShape(Rectangle())
.onTapGesture {
withAnimation {
test.toggle()
}
}

Why is my view not transitioning when first appeared on screen in swiftUI

I want a simple view to scale from 0 to 1 when the app first loads. How ever its not happening.Look at my code here:
struct ContentView: View {
#State private var isLoadng = false
var body: some View {
ZStack {
if isLoadng {
Color.red
.cornerRadius(20)
.frame(width: 150, height: 200)
.transition(.scale)
}
}
.onAppear {
withAnimation(.easeInOut(duration: 2)) {
self.isLoadng = true
}
}
}
}
The view just popping up without any transition
Here is modified part. Tested with Xcode 12
var body: some View {
ZStack {
if isLoadng {
Color.red
.cornerRadius(20)
.frame(width: 150, height: 200)
.transition(.scale)
}
}
.animation(.easeInOut(duration: 2))
.onAppear {
self.isLoadng = true
}
}

How to show or hide an overlay at a Text or Button

I do have a few items that I need to hide ( not disable ) or show depending on a value.
As for a Text() or BUtton() sample, I need to have a overlay or no overlay.
Button("how secret?", action: {
self.secretOverlay = true
})
.overlay( TopSecretOverlayView()
....
})
I did try something like
struct TopSecretOverlayView: View {
var body: some View {
HStack {
if secretOverlay {
Text("Top Secret")
.bold()
.font(.system(size: 64))
.frame(width: 350, height: 80, alignment: .center)
} else {
....
}
}
}
}
.presentation is deprecated. Not sure if that was the way.
But how should I switch a overlay between hidden and visable?
Where should an if statement look like?
As always, thank you!
Have you tried this:
Button("how secret?") {
showSecretOverlay = true
}
.overlay(TopSecretOverlayView().opacity(showSecretOverlay ? 1 : 0))
Another way of handling it is with a view builder:
struct SecretView: View {
#State private var showSecretOverlay = false
var body: some View {
Button("show password?") {
showSecretOverlay.toggle()
}
.overlay(secretOverlay)
}
#ViewBuilder private var secretOverlay: some View {
if showSecretOverlay {
TopSecretOverlayView()
}
}
}
struct TopSecretOverlayView: View {
var body: some View {
Text("password")
.foregroundColor(.white)
.background(.black)
}
}
struct SecretView_Previews: PreviewProvider {
static var previews: some View {
SecretView()
}
}
If the button tap will always have the same action, then how about setting the opacity to 0 if you want it invisible and a 1 if you want it visible?
I know this may be a little old but it seems like you could probably do what you want using an ObservedObject passed into the view.
struct TopSecretOverlayView: View {
#ObservedObject var secretVM: SecretViewModel
var body: some View {
HStack {
switch secretVM.accessType {
case .top:
Text("Top Secret")
.bold()
.font(.system(size: 64))
.frame(width: 350, height: 80, alignment: .center)
case .middle:
Text("Secret")
.bold()
.font(.system(size: 64))
.frame(width: 350, height: 80, alignment: .center)
case .low:
Text("Common Knowledge")
.bold()
.font(.system(size: 64))
.frame(width: 350, height: 80, alignment: .center)
default:
Text("Access Denied")
.bold()
.font(.system(size: 64))
.frame(width: 350, height: 80, alignment: .center)
}
}
}
}
Then where ever you change the accessType property it should cause it to update the overlay.
Button("how secret?", action: {
self.secretVM.accessType = .top
})
.overlay( TopSecretOverlayView(secretVM:self.secretVM)
....
})