I can't make many .scrollTo() in my code: (I'm French sorry for my bad english)
else if index == 1 {
withAnimation {
value.scrollTo(topID)
}
}
my issue is in value.scrollTo(topID) I tried to implement 6 of them in 3 differents foreach and The compiler doesn't compile but if I remove the value.scrollTo(topID) of one of the Foreach it compile
else if wichView.SelectedView == 5 {
HStack {
ForEach(1..<6) { index in
Spacer()
Button(action: {
if index == 3 {
withAnimation() {
wichView.SelectedView = 1
wichView.carouselLocation = 0
}
value.scrollTo(topID)
}
else if index == 1 {
withAnimation {
value.scrollTo(topID)
}
}
},
label: {
Image("IPPV5_TABBITEM\(index)")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40,
height: 40)
})
}
Spacer()
}
.padding(.bottom, -10)
.frame(width: size.width,
height: 35)
}
else if wichView.SelectedView == 6 {
HStack(spacing: size.width/3 - 10) {
Button(action: {
withAnimation {
value.scrollTo(topID)
}
},
label: {
Image("IPPV6-TABITEM1")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40,
height: 40)
})
Button(action: {
wichView.SelectedView = 1
wichView.carouselLocation = 0
value.scrollTo(topID)
},
label: {
Image("IPPV6-TABITEM2")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40,
height: 40)
})
Spacer()
}
.padding(.bottom, -10)
.frame(width: size.width - 40,
height: 35)
}
else if wichView.SelectedView == 8 {
IPPV8TabItem
}
else if wichView.SelectedView == 9 {
HStack {
ForEach(1..<6) { index in
Spacer()
Button(action: {
if index == 3 {
withAnimation {
wichView.SelectedView = 1
}
wichView.carouselLocation = 0
// withAnimation {
// value.scrollTo(topID)
// }
}
else if index == 1 {
// withAnimation {
// value.scrollTo(topID)
//}
}
},
label: {
Image("IPPV9-\(index)")
.resizable()
.aspectRatio(contentMode: .fit)
})
}
Spacer()
}
.frame(width: size.width,
height: 40)
}
This is Part of my code (all is in a ScrollView who is in a Scrollviewreader who is in a Vstack who is in a Zstack)
This code work fine and compile because I commented the .scrollTo() of one of the Foreach if I decomment it will not compile.
Any help please I don't know if the problem is the Foreachs or the .scrollTo()?
Finally I did something like else if wichView.SelectedView == 9 && wichView.SelectedView == 5 to merge both conditions and use the same .scrolLTo it works so I don't understand why it does not compile before in different Foreach
Related
I was trying to make resizable header but it breaks.
I am trying to clone twitter profile,
I think logic is right but can I know why this one is not working?
I made HStack and try to hide it but when I scroll back it can't come back.
Tried with GeometryReader
Please help me, thanks
import SwiftUI
struct ProfileView: View {
// change views
#State var isHide = false
#State private var selectionFilter: TweetFilterViewModel = .tweets
#Namespace var animation
var body: some View {
VStack(alignment: .leading) {
//hiding
if isHide == false {
headerView
actionButtons
userInfoDetails
}
tweetFilterBar
.padding(0)
ScrollView(showsIndicators: false) {
LazyVStack {
GeometryReader { reader -> AnyView in
let yAxis = reader.frame(in: .global).minY
let height = UIScreen.main.bounds.height / 2
if yAxis < height && !isHide {
DispatchQueue.main.async {
withAnimation {
isHide = true
}
}
}
if yAxis > 0 && isHide {
DispatchQueue.main.async {
withAnimation {
isHide = false
}
}
}
return AnyView(
Text("")
.frame(width: 0, height: 0)
)
}
.frame(width: 0, height: 0)
ForEach(0...9, id: \.self) { _ in
TweetRowView()
}
}
}
Spacer()
}
}
}
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
ProfileView()
}
}
extension ProfileView {
var headerView: some View {
ZStack(alignment: .bottomLeading) {
Color(.systemBlue)
.ignoresSafeArea()
VStack {
Button {
} label: {
Image(systemName: "arrow.left")
.resizable()
.frame(width: 20, height: 16)
.foregroundColor(.white)
.position(x: 30, y: 12)
}
}
Circle()
.frame(width: 72, height: 72)
.offset(x: 16, y: 24)
}.frame(height: 96)
}
var actionButtons: some View {
HStack(spacing: 12){
Spacer()
Image(systemName: "bell.badge")
.font(.title3)
.padding(6)
.overlay(Circle().stroke(Color.gray, lineWidth: 0.75))
Button {
} label: {
Text("Edit Profile")
.font(.subheadline).bold()
.accentColor(.black)
.padding(10)
.overlay(RoundedRectangle(cornerRadius: 20).stroke(Color.gray, lineWidth: 0.75))
}
}
.padding(.trailing)
}
var userInfoDetails: some View {
VStack(alignment: .leading) {
HStack(spacing: 4) {
Text("Heath Legdet")
.font(.title2).bold()
Image(systemName: "checkmark.seal.fill")
.foregroundColor(Color(.systemBlue))
}
.padding(.bottom, 2)
Text("#joker")
.font(.subheadline)
.foregroundColor(.gray)
Text("Your mom`s favorite villain")
.font(.subheadline)
.padding(.vertical)
HStack(spacing: 24) {
Label("Gothem.NY", systemImage: "mappin.and.ellipse")
Label("www.thejoker.com", systemImage: "link")
}
.font(.caption)
.foregroundColor(.gray)
HStack(spacing: 24) {
HStack {
Text("807")
.font(.subheadline)
.bold()
Text("following")
.font(.caption)
.foregroundColor(.gray)
}
HStack {
Text("200")
.font(.subheadline)
.bold()
Text("followers")
.font(.caption)
.foregroundColor(.gray)
}
}
.padding(.vertical)
}
.padding(.horizontal)
}
var tweetFilterBar: some View {
HStack {
ForEach(TweetFilterViewModel.allCases, id: \.rawValue) { item in
VStack {
Text(item.title)
.font(.subheadline)
.fontWeight(selectionFilter == item ? .semibold : .regular)
.foregroundColor(selectionFilter == item ? .black : .gray)
ZStack {
Capsule()
.fill(Color(.clear))
.frame(height: 3)
if selectionFilter == item {
Capsule()
.fill(Color(.systemBlue))
.frame(height: 3)
.matchedGeometryEffect(id: "filter", in: animation)
}
}
}
.onTapGesture {
withAnimation(.easeInOut) {
self.selectionFilter = item
}
}
}
}
}
}
While the animation between show and hide is running, the GeometryReader is still calculating values – which lets the view jump between show and hide – and gridlock.
You can introduce a new #State var isInTransition = false that checks if a show/hide animation is in progress and check for that. You set it to true at the beginning of the animation and to false 0.5 secs later.
Also I believe the switch height is not exactly 1/2 of the screen size.
So add a new state var:
#State var isInTransition = false
and in GeometryReader add:
GeometryReader { reader -> AnyView in
let yAxis = reader.frame(in: .global).minY
let height = UIScreen.main.bounds.height / 2
if yAxis < 350 && !isHide && !isInTransition {
DispatchQueue.main.async {
isInTransition = true
withAnimation {
isHide = true
}
}
// wait for animation to finish
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
isInTransition = false
}
} else if yAxis > 0 && isHide && !isInTransition {
DispatchQueue.main.async {
isInTransition = true
withAnimation {
isHide = false
}
}
// wait for animation to finish
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
isInTransition = false
}
}
return AnyView(
// displaying values for test purpose
Text("\(yAxis) - \(isInTransition ? "true" : "false")").foregroundColor(.red)
// .frame(width: 0, height: 0)
)
}
I am creating an app to give reward stickers for kids. When 10 stickers are collected, I want all stickers are removed from the main view and the image will be shown before removing everything. This image is shown at the 11th click of the button but navigation view to select stickers are also shown on top of this image, which I do not want to be popped up.
Here is my code...
NavigationView {
ZStack {
Image("Greenbase")
.resizable()
.frame(width: self.screenWidth, height: self.screenWidth)
ZStack {
ForEach($stickers) { $sticker in
VStack {
Image(sticker.name)
.resizable()
.frame(width: self.screenWidth*0.2, height: self.screenWidth*0.2)
.offset(x: self.selectedSticker == sticker ? sticker.offset.width + self.dragOffset.width : sticker.offset.width,
y: self.selectedSticker == sticker ? sticker.offset.height + self.dragOffset.height : sticker.offset.height)
.gesture(
DragGesture()
.updating(self.$dragOffset, body: { (value, state, transaction) in
if nil == self.selectedSticker {
self.selectedSticker = sticker
}
state = value.translation
})
.onEnded { value in
sticker.offset.height += value.translation.height + dragOffset.height
sticker.offset.width += value.translation.width + dragOffset.width
self.selectedSticker = nil
}
)
}//vstack
.sheet(isPresented: $isAddingSticker, onDismiss: addSticker) {
StampView(sticker: $selectedSticker)
}//foreach
if counter < 11 {
Button {
isAddingSticker = true
print("button pressed: \(counter)")
counter += 1
}label: {
RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color(red: 55/255, green: 266/255, blue: 213/255))
.frame(width: 300, height: 40, alignment: .center)
.overlay(
Text(" ステッカーをはろう!")
.foregroundColor(Color(red: 41/255, green: 52/255, blue: 98/255))
.font(.title2))
}//button label
}else {
Button{
isAddingSticker = false
counter = 0
removeSticker(at: 1)
}label: {
Image("WellDone")
.resizable()
.frame(width: self.screenWidth*0.5, height: self.screenWidth*0.5)
.scaleEffect(2.0)
.rotationEffect(Angle(degrees: 360 ))
.animation(.easeInOut(duration: 3.0))
}//button label
} //else
}//Hstack
.disabled(isEditing)
}
}//zstack stamps
}//zstac w bc image
}//body
func removeSticker(at index: Int) {
self.stickers.removeAll()//< -- Here
}
func addSticker() {
guard let name = selectedSticker else { return }
withAnimation {
stickers.insert(name, at: 0)
}
}
}//struct
I am still super new to Swift... the question should be very basic but hopefully can find the answer.
I don't think I understand the question correctly, but you can hide the navigation with this modifier at the end of the navigation view.
NavigationView {
...
}.navigationBarHidden(true)
I have a scrollview inside a camera view and I need to apply opacity in the top of scrollview
Actual / Expected behavior
I already tried it without success.
var body: some View {
ZStack(alignment: .top) {
VStack {
}
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: 10
)
.opacity(0.5)
ScrollView(.vertical, showsIndicators: false) {
ScrollViewReader { scrollView in
VStack(alignment: .leading, spacing: 16) {
ForEach(messages) { message in
LazyVStack(alignment: .leading, spacing: 4) {
Text("\(message.sender)")
.kerning(0.02)
.fontWeight(/*#START_MENU_TOKEN#*/.bold/*#END_MENU_TOKEN#*/)
.font(.system(size: 13))
.lineSpacing(21)
.foregroundColor(Color.white)
Text("\(message.text)")
.kerning(0.02)
.fontWeight(.medium)
.font(.system(size: 13))
.lineSpacing(21)
.foregroundColor(Color.white)
}
}
}
.onChange(of: messages) { _ in
scrollView.scrollTo(messages.count + 1, anchor: .bottom)
}
.onAppear {
scrollView.scrollTo(messages.count, anchor: .bottom)
}
}
}
}
}
How can I apply opacity at top of this scrollview?
I solved this issue with the following code
func getNameOpacity(_ distance: CGFloat) -> Double {
return distance < 3 ? 1 : distance < 5 ? 0.7 : distance < 7 ? 0.5 : distance < 9 ? 0.3 : 0
}
func getTextOpacity(_ distance: CGFloat) -> Double {
return distance < 23 ? 1 : distance < 25 ? 0.7 : distance < 27 ? 0.5 : distance < 29 ? 0.3 : 0
}
var body: some View {
GeometryReader { outsideProxy in
ScrollView(.vertical, showsIndicators: false) {
ScrollViewReader { scrollView in
VStack(alignment: .leading, spacing: 16) {
ForEach(messages) { message in
GeometryReader { insideProxy in
let distanceFromTop = outsideProxy.frame(in: .global).minY - insideProxy.frame(in: .global).minY + 6
let nameOpacity = getNameOpacity(distanceFromTop)
let textOpacity = getTextOpacity(distanceFromTop)
VStack(alignment: .leading, spacing: 4) {
Text("\(message.sender)")
.kerning(0.02)
.fontWeight(.bold)
.font(.system(size: 13))
.lineSpacing(21)
.foregroundColor(Color.white)
.opacity(nameOpacity)
Text("\(message.text)")
.kerning(0.02)
.fontWeight(.medium)
.font(.system(size: 13))
.lineSpacing(21)
.foregroundColor(Color.white)
.opacity(textOpacity)
}
}
}
.frame(width: 100, height: 35, alignment: .center)
}
.onChange(of: messages) { _ in
scrollView.scrollTo(messages.count + 1, anchor: .bottom)
}
.onAppear {
scrollView.scrollTo(messages.count, anchor: .bottom)
}
}
}
}
}
I'm currently working on trying to implement a custom dropdown menu in SwiftUI that displays a grid of buttons (1-16) and allows you to select one of them. I am using an overlay to display the dropdown below the corresponding button, and it seems to be functioning properly except it's displaying the dropdown below all of the other elements in the view. I found another post here regarding this issue and they used a ZStack to solve it, but I haven't been able to get the same success. Does anyone have any ideas on how to fix this? Here's my code:
struct ContentView: View {
#State var showDropdown = false
#State var selected = 0
var body: some View {
ZStack {
HStack(spacing: 30) {
VStack(alignment: .leading, spacing: 10) {
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { showDropdown.toggle() }) {
Text(selected == 0 ? "Omni" : String(selected))
.frame(width: 80, height: 36)
}
.zIndex(1)
.overlay(
VStack(spacing: 0) {
if self.showDropdown {
Spacer(minLength: 26)
DropdownMenu(selection: self.$selected)
} else {
EmptyView()
}
}, alignment: .topLeading
)
}
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { }) {
Text("Omni")
.frame(width: 80, height: 36)
}
}
}
VStack(alignment: .leading, spacing: 10) {
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { }) {
Text("Omni")
.frame(width: 80, height: 36)
}
}
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { }) {
Text("Omni")
.frame(width: 80, height: 36)
}
}
}
}
}
.padding()
.frame(maxHeight: .infinity)
.buttonStyle(PlainButtonStyle())
}
}
and here are some images of the results:
Before Press
After Press
Thank you in advance!
The .zIndex works for view in same container, so you need something like the following (or make custom flat layout container with all such buttons at same level, then rise clicked button atop).
VStack(alignment: .leading, spacing: 10) {
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { showDropdown.toggle() }) {
Text(selected == 0 ? "Omni" : String(selected))
.frame(width: 80, height: 36)
}
.zIndex(1)
.overlay(
VStack(spacing: 0) {
if self.showDropdown {
Spacer(minLength: 26)
DropdownMenu(selection: self.$selected)
}
}, alignment: .topLeading
)
}.zIndex(showDropdown ? 1 : 0) // << this !!
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { }) {
Text("Omni")
.frame(width: 80, height: 36)
}
}
}.zIndex(showDropdown ? 1 : 0) // << and this !!
I'm working on a checklist app that has several arrays with checks. I'd like to save the state if a users closes/quits the app. I was thinking of using the UserDefault methods for this:
HStack {
ForEach(0 ..< checklist.steps) { index in
VStack {
Button(action: {
self.checked[index].toggle()
UserDefaults.standard.set(self.checked[index], forKey: "Check")
I'm currently using the following state for checks:
#State private var checked = [false, false, false, false, false, false]
Does anyone know how to apply UserDefaults for arrays or generally how to save the state for your app when closing it?
Thanks in advance!
try this: (it is the code from last time ;)) i think the true/false settings itself is not correct, but the saving / loading works ;)
struct ChecklistView: View {
var checklist: Checklist
#State var currentProgress: Float = 0.0
#State var checked : [Bool] = [false, false, false, false]
init(checklist: Checklist) {
self.checklist = checklist
}
func saveUserDefaults() {
var value = ""
for eachValue in checked {
if eachValue == true { value += "1" }
else { value += "0" }
}
UserDefaults.standard.set(value, forKey: checklist.title)
}
var body: some View {
ZStack(alignment: .leading) {
// RoundedRectangle(cornerRadius: 20)
// .foregroundColor(.red).opacity(0.5)
// .frame(width: 200)
VStack {
HStack(alignment: .top) {
checklist.image
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
.padding(.trailing)
VStack(alignment: .leading) {
Text(checklist.title)
.font(.system(size: 16, weight: .medium))
.padding(.bottom, 4)
Text(checklist.instruction.uppercased()).font(.system(size: 12))
HStack {
ForEach(0 ..< checklist.steps) { index in
VStack {
Button(action: {
self.checked[index].toggle()
print(self.checked)
self.saveUserDefaults()
}) {
ZStack {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(self.checked[index] ? Color("LightGreen") : .gray )
.frame(width: 40, height: 40)
Image(systemName: self.checked[index] ? "checkmark" : "plus")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 16, height: 16)
.foregroundColor(self.checked[index] ? .white : .white)
}
}
}
}
}
}
}.frame(width: 350, alignment: .leading)
}
}
.onAppear() {
if let values = UserDefaults.standard.value(forKey: self.checklist.title) as? String {
self.checked = values.map {
if $0 == "1" { return true }
return false
}
}
print(self.checked)
}
.frame(width: 350)
.padding(.bottom, 16)
.cornerRadius(8)
.padding(.top, 20)
}
}
I haven't test that solution, but can't you store your array in your NSUserdefaults after every button action?
Button(action: {
self.checked[index].toggle()
UserDefaults.standard.set(self.checked, forKey: "Check")
Probably, the best solution would be using CoreData for that, which would be very easier to use.
PS: I can test that solution later on.. not at my Mac right now