Unfortunately, it does not work. When I tap one of the cells it does not do anything. My .sheet is outside of ScrollView
ScrollView(.vertical, showsIndicators: true) {
VStack(spacing: 0) {
ForEach(service.authorPost, id: \.author_id) { post in
AuthorListElementView(authorPost: post)
.onTapGesture {
self.shown.toggle()
self.selectedAuthor = post
}
.onAppear {
self.service.loadMoreContentIfNeeded(currentItem: post)
}
}
}
if service.isLoadingPage {
ProgressView()
.padding()
.offset(x: UIScreen.main.bounds.width / 3)
}
}
.sheet(isPresented: self.$shown, content: { PopupAuthorView(authorPost: selectedAuthor)})
Because I had another .onTapGesture inside the AuthorListElement. When I deleted the one in the AuthorListElement it works.
Related
I am new to the WatchOS world and because of that I am in need of some help getting past a point where, even with tutorials, I seem to not be able to do what I am needing to do.
I have a simple watchOS app. Basically its a list with toggles and 2 buttons. What I am wanting to do is, when clicking on said button, have all the toggles change to "off" state. Likewise, the other button will put them all to the "on" state.
Here is my code so far (some code was removed):
import SwiftUI
struct User: Decodable {
var theID: String
var name: String
var description: String
var isOn: Bool
}
var body: some View {
List{
ForEach($users, id: \.theID) { $item in
HStack {
Toggle(isOn: $item.isOn) {
Label {
Text(item.description)
.frame(maxWidth: 85)
.frame(maxHeight: 100)
.frame(maxWidth: .infinity, alignment: .trailing)
} icon: {
if item.name == "J" {
Image("imgJayden")
.scaledToFill()
} else if item.name == "T" {
Image("imgTab")
.scaledToFill()
} else {
Image("imgBoth")
.scaledToFill()
}
}
.onChange(of: item.isOn) { value in
if (rndCnt == 0) {
if (value) {
//print("tunning on")
sendJsonData(a: item.theID, b: item.name, c: item.description, d: item.isOn)
} else {
//print("turning off")
sendJsonData(a: item.theID, b: item.name, c: item.description, d: item.isOn)
}
rndCnt = 1
} else {
rndCnt = 0
}
}
}
}.padding(2)
}
List{
HStack (alignment: .center, spacing: 50){
Button {
}label: {
Image("on")
.scaledToFill()
.frame(maxWidth: .infinity, alignment: .center)
}
.onTapGesture {
print("turned all on!")
}
Button {
}label: {
Image("off")
.scaledToFill()
.frame(maxWidth: .infinity, alignment: .center)
}
.onTapGesture {
print("turned all off!")
}
}
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "http://192.168.1.1:81/data.php?data=ALL") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {data, response, error in
if let data = data {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let decodedResponse = try?
decoder.decode([User].self, from: data) {
DispatchQueue.main.async {
self.users = decodedResponse
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error....")")
}.resume()
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
For my list I have the .onAppear(perform: loadData) which is what populates the toggles with the data (thats in function loadData. Now I'm at a standstill on how to loop through all the toggles and set them all to either on or off depending on which button was pressed.
Would be great if someone could help me out on this!
remove the onTapGesture for the Button, they don't need it they are buttons.
Use this example code to turn all users Toggle on/off.
List{
HStack (alignment: .center, spacing: 50){
Button {
print("turned all on!")
users.indices.forEach { users[$0].isOn = true } // <-- here
// alternative
// for i in users.indices { users[i].isOn = true }
}label: {
Image("on")
.scaledToFill()
.frame(maxWidth: .infinity, alignment: .center)
}
Button {
print("turned all off!")
users.indices.forEach { users[$0].isOn = false } // <-- here
}label: {
Image("off")
.scaledToFill()
.frame(maxWidth: .infinity, alignment: .center)
}
}.buttonStyle(.bordered) // <-- here
}
Progress View doesn't show on second + load when trying to do pagination. When I scroll to the bottom the progress view will appear once. But all the other times it doesn't. This only seems to occur when im using some sort of animation.
If I just have a static text like "Loading..." it works as expected. I added the section group where it checks a condition to verify if it should be presented or not. Not sure if I'm supposed to use something like "stop animating" like the loading indicator has in UIKit
struct ContentView: View {
#State var movies: [Movie] = []
#State var currentPage = 1
#State private var isLoading = false
var body: some View {
NavigationView {
List {
Section {
} header: {
Text("Top Movies")
}
ForEach(movies) { movie in
HStack(spacing: 8) {
AsyncImage(url: movie.posterURL, scale: 5) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100)
.cornerRadius(10)
} placeholder: {
ProgressView()
.frame(width: 100)
}
VStack(alignment: .leading, spacing: 10) {
Text(movie.title)
.font(.headline)
Text(movie.overview)
.lineLimit(5)
.font(.subheadline)
Spacer()
}
.padding(.top, 10)
}
.onAppear {
Task {
//Implementing infinite scroll
if movie == movies.last {
isLoading = true
currentPage += 1
movies += await loadMovies(page: currentPage)
isLoading = false
}
}
}
}
Section {
} footer: {
if isLoading {
HStack {
Spacer()
ProgressView()
.tint(.green)
Spacer()
}
} else {
EmptyView()
}
}
}
.navigationTitle("Movies")
.navigationBarTitleDisplayMode(.inline)
.listStyle(.grouped)
.task {
movies = await loadMovies()
}
.refreshable {
movies = await loadMovies()
}
}
}
}
even when I look at the view hierarchy its like the progress view square is there but the icon / loading indicator isn't showing:
If I add the overlay modifier it works but I don't like doing this because when I scroll back up before the content finishes loading the spinner is above the list view:?
.overlay(alignment: .bottom, content: {
if isLoading {
HStack {
Spacer()
ProgressView()
.tint(.green)
Spacer()
}
} else {
EmptyView()
}
})
We also had this problem, we think it is a bug of ProgressView. Our temporary correction is to identify the ProgressView with a unique id in order to render it again.
struct ContentView: View {
#State var id = 0
var body: some View {
ProgressView()
.id(id)
.onAppear {
...
id += 1
}
}
}
I have an HStack of circles in SwiftUI, and the number of circles is determined based on the length of an array, like this:
#State var myArr = [...]
...
ScrollView(.horizontal) {
HStack {
ForEach(myArr) { item in
Circle()
//.frame(...)
//.animation(...) I tried this, it didn't work
}
}
}
Then I have a button that appends an element to this array, effectively adding a circle to the view:
Button {
myArr.append(...)
} label: {
...
}
The button works as intended, however, the new circle that is added to the view appears very abruptly, and seems choppy. How can I animate this in any way? Perhaps it slides in from the side, or grows from a very small circle to its normal size.
You are missing transition, here is what you looking:
struct ContentView: View {
#State private var array: [Int] = Array(0...2)
var body: some View {
ScrollView(.horizontal) {
HStack {
ForEach(array, id:\.self) { item in
Circle()
.frame(width: 50, height: 50)
.transition(AnyTransition.scale)
}
}
}
.animation(.default, value: array.count)
Button("add new circle") {
array.append(array.count)
}
Button("remove a circle") {
if array.count > 0 {
array.remove(at: array.count - 1)
}
}
}
}
a version with automatic scroll to the last circle:
struct myItem: Identifiable, Equatable {
let id = UUID()
var size: CGFloat
}
struct ContentView: View {
#State private var myArr: [myItem] = [
myItem(size: 10),
myItem(size: 40),
myItem(size: 30)
]
var body: some View {
ScrollViewReader { scrollProxy in
VStack(alignment: .leading) {
Spacer()
ScrollView(.horizontal) {
HStack {
ForEach(myArr) { item in
Circle()
.id(item.id)
.frame(width: item.size, height: item.size)
.transition(.scale)
}
}
}
.animation(.easeInOut(duration: 1), value: myArr)
Spacer()
Button("Add One") {
let new = myItem(size: CGFloat.random(in: 10...100))
myArr.append(new)
}
.onChange(of: myArr) { _ in
withAnimation {
scrollProxy.scrollTo(myArr.last!.id, anchor: .trailing)
}
}
.frame(maxWidth: .infinity, alignment: .center)
}
.padding()
}
}
}
I was trying to to do transition animation Scrollview but found out that how things work differently in scrollView. But still unable to do animation in it. I am providing code, please have a look. Using Xcode 11 beta 6
import SwiftUI
struct ContentView : View {
#State private var isButtonVisible = false
var body: some View {
NavigationView {
ScrollView{
VStack {
Button(action: {
// withAnimation {
self.isButtonVisible.toggle()
// }
}) {
Text("Press me")
}
if isButtonVisible {
Text("sss")
.frame(height: true ? 50 : 0, alignment: .center)
.background(Color.red)
.animation(.linear(duration: 2))
// .transition(.move(edge: .top))
}
}
}
}
}}
This must be a bug, and I suggest you file a bug report with Apple. I find a workaround (see code below), but it unfortunately uncovers another bug!
In order to make the animation inside the ScrollView to work, you can encapsulate the contents in a custom view. That will fix that problem.
This will uncover a new issue, which is made evident by the borders I added to your code: when the Text view is added, it shifts parts of the contents outside the ScrollView.
You will see that this is not correct. Try starting your app with a default value isButtonVisible = true, and you will see it renders it differently.
struct ContentView : View {
var body: some View {
NavigationView {
ScrollView {
EncapsulatedView().border(Color.green)
}.border(Color.blue)
}
}
}
struct EncapsulatedView: View {
#State private var isButtonVisible = false
var body: some View {
VStack {
Text("Filler")
Button(action: {
withAnimation(.easeInOut(duration: 2.0)) {
self.isButtonVisible.toggle()
}
}) {
Text("Press me")
}
if isButtonVisible {
Text("sss")
.frame(height: true ? 50 : 0, alignment: .center)
.transition(.opacity)
.background(Color.red)
}
Spacer()
}.border(Color.red)
}
}
Updated to Xcode beta-3, Popover was deprecated... having one hell of a time trying to figure out how to make it work again!?!?
It no longer "pops up" it slides up from the bottom.
It's no longer positioned or sized correctly, takes up the whole screen.
Once dismissed, it never wants to appear again.
This was the old code, that worked perfectly...
struct ExerciseFilterBar : View {
#Binding var filter: Exercise.Filter
#State private var showPositions = false
var body: some View {
HStack {
Spacer()
Button(action: { self.showPositions = true } ) {
Text("Position")
}
.presentation(showPositions ? Popover(content: MultiPicker(items: Exercise.Position.allCases, selected:$filter.positions),
dismissHandler: { self.showPositions = false })
: nil)
}
.padding()
}
}
And this is the new code...
struct ExerciseFilterBar : View {
#Binding var filter: Exercise.Filter
#State private var showPositions = false
var body: some View {
HStack {
Spacer()
Button(action: { self.showPositions = true } ) {
Text("Position")
}
.popover(isPresented: $showPositions) {
MultiPicker(items: Exercise.Position.allCases, selected:self.$filter.positions)
.onDisappear { self.showPositions = false }
}
}
.padding()
}
}
I ended up using PresentationLink just so I can move forward with everything else...
struct ExerciseFilterBar : View {
#Binding var filter: Exercise.Filter
var body: some View {
HStack {
Spacer()
PresentationLink(destination: MultiPicker(items: Exercise.Position.allCases, selected:$filter.positions)) {
Text("Position")
}
}
.padding()
}
}
It works, as far as testing is concerned, but it's not a popover.
Thanks for any suggestions!
BTW, this code is being in the iPad simulator.
On OSX the code below works fine
struct ContentView : View {
#State var poSelAbove = false
#State var poSelBelow = false
#State var pick : Int = 1
var body: some View {
let picker = Picker(selection: $pick, label: Text("Pick option"), content:
{
Text("Option 0").tag(0)
Text("Option 1").tag(1)
Text("Option 2").tag(2)
})
let popoverWithButtons =
VStack {
Button("Not Dismiss") {
}
Divider()
Button("Dismiss") {
self.poSelAbove = false
}
}
.padding()
return VStack {
Group {
Button("Show button popover above") {
self.poSelAbove = true
}.popover(isPresented: $poSelAbove, arrowEdge: .bottom) {
popoverWithButtons
}
Divider()
Button("Show picker popover below") {
self.poSelBelow = true
}.popover(isPresented: $poSelBelow, arrowEdge: .top) {
Group {
picker
}
}
}
Divider()
picker
.frame(width: 300, alignment: .center)
Text("Picked option: \(self.pick)")
.font(.subheadline)
}
// comment the line below for iOS
.frame(width: 800, height: 600)
}
On iOS (iPad) the popover will appear in a strange transparent full screen mode. I don't think this is intended. I have added the problem to my existing bug report.