List inside ScrollView is not displayed on WatchOs - swift

I have a list inside a scrollview and it is not displaying below the image and the buttons. I've also tried to put the list and other items inside of a VStack and that allows me to see one item of the list at the time opposed to scrolling past the Image and buttons to show the whole list.
ScrollView{
Image(uiImage: self.image)
.resizable()
.frame(width: 80, height: 80)
.scaledToFit()
Text("\(name)")
.lineLimit(2)
HStack{
Button(action: {
print("button1")
}){
Image(systemName: "pencil")
}
Button(action: {
print("button 2")
}){
Image(systemName: "trash")
}
}
List{
ForEach(self.items, id: \.self) { item in
VStack{
Text(item.name)
.font(.headline)
.lineLimit(1)
Text(item.subname)
.font(.subheadline)
.lineLimit(1)
}
}
}
}
.navigationBarTitle(Text("Tittle"))
.edgesIgnoringSafeArea(.bottom)
Ive also tried to add .frame( minHeight: 0, maxHeight: .infinity)
to the list to force it to have its whole height and that did not work either. Any suggestions or might this be a swiftUI bug ?
EDIT
I just realized Im getting this error when scrolling:
APPNAME Watch Extension[336:60406] [detents] could not play detent NO, 2, Error Domain=NSOSStatusErrorDomain Code=-536870187 "(null)", (
{
Gain = "0.01799999922513962";
OutputType = 0;
SlotIndex = 4;
},
{
Gain = "0.6000000238418579";
OutputType = 1;
SlotIndex = 5;
}
)

Indicate some height for your List like
List{
ForEach(self.items, id: \.self) { item in
VStack{
Text(item.name)
.font(.headline)
.lineLimit(1)
Text(item.subname)
.font(.subheadline)
.lineLimit(1)
}
}
}.frame(minHeight: 200, maxHeight: .infinity)

Have you tried putting the List inside of a GeometryReader and setting the frame there?

I got the same error with UIKit. The error message is related to haptic feedback, see here.
It says that a haptic feedback could not be played, probably type 2, i.e. directionDown, see here.
Since my code does not call play(_:), it must be called by watchOS itself.
The reason for the error might be very fast scrolling, which could result in too frequent calls to play(_:) that cannot be handled properly. The docs say:
Do not call this method multiple times in quick succession. If the
haptic engine is already engaged when you call this method, the system
stops the current feedback and imposes a minimum delay of 100
milliseconds before engaging the engine to generate the new feedback.
If this is really an effect of watchOS, I guess you cannot do anything to avoid the error.

Related

How to align two items in a box on different sites in SwiftUI

I am currently working on a little Project and I really dont know how to fix some issue, the problem is that i have no idea how to align two items in a horizontal stack on different sites.
Heres a screenshot of the current state:
Current State
and that's how it should look like:
How it should look like
this is the current code:
var body: some View {
Button {
}
label: {
HStack{
VStack(alignment: .leading) {
Text("Max Mustermann")
Text("Mustermann GmbH")
.fontWeight(.thin)
}
Image(systemName: "arrow.forward.circle")
}
.frame(minWidth: 320, minHeight: 50, alignment: .leading)
.foregroundColor(.black)
.padding()
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(.gray, lineWidth: 1))
}
}
}
I hope somebody can help me :)
Thank you!
Seems like a Spacer is what you need :) Just add it in between the Views in the HStack like so:
HStack{
VStack(alignment: .leading) {
Text("Max Mustermann")
Text("Mustermann GmbH")
.fontWeight(.thin)
}
Spacer() // The New Spacer Here
Image(systemName: "arrow.forward.circle")
}
The documentation linked above illustrates pretty well what can be achieved by using a Spacer :)
Good luck!

Unable to create list of string SwiftUI from json response

I have a list of remarks which I receive from an API call. I have created a loop to create TEXT from the remarks but the texts do not appear in the application. Please check the code.
I have also checked the debugger and it shows a the string present there but it doesn't create the text.
VStack{
Text("Remarks")
.padding(.horizontal, 15)
.font(.title2)
if(currentFrResponse.remarks != nil){
Text("fine")
List(currentFrResponse.remarks!, id:\.self){ currentRemark in
Text("Test")
Text(currentRemark)
.bold()
.padding()
}
}
}
debugger result
Let me know if you need any more details
I have tried this code in my project and it's working fine, as per my knowledge this issue is something else related to other component or view that you are using in the same controller, But if you want you can try creating individual view for this list and then use it.
var body: some View {
VStack{
Text("Remarks")
.padding(.horizontal, 15)
.font(.title2)
List(landmarks, id:\.self){ currentRemark in
VStack{
Text(currentRemark.name)
.bold()
.padding()
}.padding()
}
}
}

SwiftUI Finding Solution for Button overlay on Image

I overlayed a button/text on the image, but the button didnt respond. Is there any solution?
It seems like the hidden navigation bar view is blocking the button or the image is blocking the touch gesture. How should I fix this?
struct Tutorial: View {
let dataModel = DataModel()
#State var messageIndex = 0
var body: some View{
ZStack{
Image("Tutorial UI")
.resizable()
.scaledToFill()
.overlay(
VStack(spacing:20){
VStack(spacing:30){
HStack{
Spacer()
Text("Next")
.foregroundColor(.blue)
.fontWeight(.black)
.onTapGesture(count:1) {
if self.messageIndex != 4{
self.messageIndex += 1
}else{
self.messageIndex = 0
}
}
}
Text(dataModel.tutorialMessage[messageIndex][0])
.fontWeight(.black)
.font(.largeTitle)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .center)
}.padding()
Text(dataModel.tutorialMessage[messageIndex][1])
.font(.title)
Spacer()
}.padding(.horizontal, 48)
.padding(.top, 50)
)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity,
alignment: .center)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
}
What #Asperi was getting at is you have provided a code snippet that depends upon a model we know nothing about. When you post code, you should post a simplified version. In this case I took your code and swapped out your data model for a simple array. I also downloaded an image to use from NASA's Image of the Day to use. And, Asperi nailed the problem with his second question...
Looking at it, the first thing that struck me is you are trying too hard to position things just so in SwiftUI. It doesn't work that way. And that led to your problems. I pulled out your frames which actually don't do anything in this layout. I also pulled out the specific padding amounts. I then pulled the Spacer() out that was in your HStack, and your "Next" button appeared on the screen. I suspect that setting the image as .scaledToFill() and an image that is not in the same dimensions of the screen caused the view to be larger than the screen you could see. Putting the Spacer() in caused your "Next" button to move all of the way to the right, which was off screen. Once I could see it, I was able to click it and it worked fine.
You have an extremely complicated structure that produces a pretty simple layout. Next time, start small and only add what you need. Less layout is better in SwiftUI, or you end up with unintended consequences. Try changing the HStack in your Overlay to the following and then work from there, one step at a time:
HStack{
Text("Next")
.foregroundColor(.blue)
.fontWeight(.black)
.onTapGesture(count:1) {
if self.messageIndex != 4{
self.messageIndex += 1
}else{
self.messageIndex = 0
}
}
}
Remember, in SwiftUI the Child View determines its own size. Don't try to force things from a Parent.

SwiftUI - Navigation Link transition is slow when tapping on it

I'm developing a SwiftUI App with Xcode 12.0.
My HomeView simply contains a NavigationLink (within a NavigationView) that push to a second View containing a List of more than 4.000 elements previously downloaded from a server (when tapping the navigation link the download is already completed).
The issue is that when I tap it takes almost 2 seconds to show the next page. The same thing happen when I tap the iOS Back button.
In other hands, doing the same thing without a big List makes it fast as it should always be.
The HomeView Navigation link is this:
ScrollView(.vertical, showsIndicators: true, content: {
ForEach(firstHomeCard) { homeCard in
NavigationLink(destination: ExoplanetsDatabaseView()) {
HomeCard(homeCard: homeCard, geo: geo.size.width)
}.buttonStyle(PlainButtonStyle())
}
}
This is my List code in the second View:
List {
ForEach(HttpResponseHolder.shared.exoplanets.filter({ searchText.isEmpty ? true : $0.pl_name.contains(searchText) }), id: \.self) { exoplanet in
NavigationLink(destination: ExoplanetDetailView(exoplanet: exoplanet)) {
ExoplanetCard(exoplanet: exoplanet)
}.buttonStyle(PlainButtonStyle())
}.listRowBackground(Color.clear)
.padding(.trailing, -15)
.padding(.leading, 2)
.padding(.top, 2)
.padding(.bottom, 2)
}.id(UUID())
Is that a SwiftUI bug that can't handle many elements in a List?
UPDATE
ExoplanetCard's body
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 22.0)
.opacity(0.8)
.foregroundColor(Color(red: 31/255, green: 40/255, blue: 45/255))
HStack(alignment:.center, spacing: 20, content: {
Spacer()
imageForExoplanet(exoplanet: exoplanet)
.resizable()
.frame(width: 120, height: 120, alignment:.center)
.aspectRatio(contentMode: .fit)
.opacity(0.9)
.clipShape(Circle())
Divider()
VStack(spacing:5) {
Text(exoplanet.pl_name)
.foregroundColor(.white)
.font(.headline)
.lineLimit(2)
planetTypeTextReturner(exoplanet: exoplanet)
.foregroundColor(.white)
.font(.body)
.lineLimit(2)
}
Spacer()
})
}
}
ExoplanetDetailView is just an empty view so far.
EDIT 2
So by making my data model conforming to Identifiable e by giving it an id property of type UUID() and removing the id:.self the performance are now better. Still not perfect but at least improved a bit.

SwiftUI: Two buttons with the same width/height

I have 2 buttons in an H/VStack. Both of them contain some text, in my example "Play" and "Pause". I would like to have that both buttons have the same width (and height) determined by the largest button. I have found some answers right here at SO but I can't get this code working unfortunately.
The following code illustrates the question:
import SwiftUI
struct ButtonsView: View {
var body: some View {
VStack {
Button(action: { print("PLAY tapped") }){
Text("Play")
}
Button(action: { print("PAUSE tapped") }) {
Text("Pause")
}
}
}
}
struct ButtonsView_Previews: PreviewProvider {
static var previews: some View {
ButtonsView()
}
}
The tvOS preview from Xcode shows the problem:
I would be thankful for an explanation for newbies 🙂
Here is run-time based approach without hard-coding. The idea is to detect max width of available buttons during drawing and apply it to other buttons on next update cycle (anyway it appears fluently and invisible for user).
Tested with Xcode 11.4 / tvOS 13.4
Required: Simulator or Device for testing, due to used run-time dispatched update
struct ButtonsView: View {
#State private var maxWidth: CGFloat = .zero
var body: some View {
VStack {
Button(action: { print("PLAY tapped") }){
Text("Play")
.background(rectReader($maxWidth))
.frame(minWidth: maxWidth)
}.id(maxWidth) // !! to rebuild button (tvOS specific)
Button(action: { print("PAUSE tapped") }) {
Text("Pause Long Demo")
.background(rectReader($maxWidth))
.frame(minWidth: maxWidth)
}.id(maxWidth) // !! to rebuild button (tvOS specific)
}
}
// helper reader of view intrinsic width
private func rectReader(_ binding: Binding<CGFloat>) -> some View {
return GeometryReader { gp -> Color in
DispatchQueue.main.async {
binding.wrappedValue = max(binding.wrappedValue, gp.frame(in: .local).width)
}
return Color.clear
}
}
}
You can implement the second custom layout example in the WWDC 2022 talk https://developer.apple.com/videos/play/wwdc2022/10056/ titled "Compose custom layouts with SwiftUI" which, if I understand the question, specifically solves it, for an arbitrary number of buttons/subviews. The example starts at the 7:50 mark.
after reading hit and trial implementing SO solns etc finally resolved this issue posting so that newbies as well as intermediate can benefit
paste it and obtain equal size(square) views
VStack(alignment: .center){
HStack(alignment:.center,spacing:0)
{
Button(action: {}, label: {
Text("Button one")
.padding(35)
.foregroundColor(.white)
.font(.system(size: 12))
.background(Color.green)
.frame(maxWidth:.infinity,maxHeight: .infinity)
.multilineTextAlignment(.center)
.cornerRadius(6)
}).background(Color.green)
.cornerRadius(6)
.padding()
Button(action: {}, label: {
Text("Button two")
.padding(35)
.foregroundColor(.white)
.font(.system(size: 12))
.frame(maxWidth:.infinity,maxHeight: .infinity)
.background(Color.green)
.multilineTextAlignment(.center)
}) .background(Color.green)
.buttonBorderShape(.roundedRectangle(radius: 8))
.cornerRadius(6)
.padding()
}.fixedSize(horizontal: false, vertical: true)
Add as many as buttons inside it. You can adjust it for VStack by adding only one button in hstack and add another button in another Hstack. I gave a general soln for both VStack and Hstack. You can also adjust padding of button as .padding(.leading,5) .padding(.top,5) .padding(.bottom,5) .padding(.trailing,5) to adjust the gaps between buttons
I think the best solution is to use GeometryReader, which resizes the width of the content of the Button. However, you need to check that you set a width of the Wrapper around the GeometryReader, because otherwise it would try to use the full screen width. (depends where you use that view, or if it is your primary view)
VStack
{
GeometryReader { geo in
VStack
{
Button(action: { print("PLAY tapped") }){
Text("Play")
.frame(width: geo.size.width)
}
.border(Color.blue)
Button(action: { print("Pause tapped") }){
Text("PAUSE")
.frame(width: geo.size.width)
}
.border(Color.blue)
}
}
}
.frame(width: 100)
.border(Color.yellow)
... which will look like that.
What happens if you put a Spacer() right after the Text("Play")? I think that might stretch out the 'Play' button.
Or maybe before and after Text("Play").