Clicking a button highlights the while vertical stack in SwiftUI - ios

I am following https://www.youtube.com/watch?v=Xetrbmnszjc tutorial to learn SwiftUI. In a view, we have a vertical stack, which has some text and a button at the bottom. When I click the button, the whole of the vertical stack view is highlighted. I cannot understand why this happens.
VStack {
Text(drink.description)
.foregroundColor(.primary)
.font(.body)
.lineLimit(nil)
.lineSpacing(12)
HStack{
Spacer()
OrderButton()
Spacer()
}.padding(.top,25)
}.padding(.top)
.padding(.bottom)
}
struct OrderButton: View{
var body: some View{
Button(action:{}){
Text("Order Now")
}.frame(width:200,height:50)
.font(.headline)
.foregroundColor(.white)
.background(Color.black)
.cornerRadius(10)
}
}
Please help on how to fix it. Since SwiftUI is relatively new, it becomes difficult to find answers to problems.
The attached image shows the scenario when I click on the Order Now button.

Try this:
Button(action:{}){
Text("Order Now")
.frame(width:200,height:50)
.font(.headline)
.foregroundColor(.white)
.background(Color.black)
}.buttonStyle(PlainButtonStyle())
.cornerRadius(10)
You need to change view inside button, not frame itself. View which you put in there sets frame to all button automatically, and clickable plane also be the same frame as view.

Related

SwiftUI - can I make one element in Form fill the whole screen width (without horizontal margins)?

I would like a single item inside SwiftUI Form to run from side to side, without having Form's default margins.
Unfortunately, whatever I do (like ading a wider .frame, negative horizontal padding, or .offset), the team image view seems to be always cropped by the form to fit the form area (= has horizontal margins).
Is it possible to make the Image below touch the left and right side of the screen?
I am using Form for my app settings, but I would like to add a full-width section there (think eg. a banner to promote a feature).
SwiftUI Playgrounds code:
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
var body: some View {
Form {
Section(
header: Text("First section")
) {
Text("Hello world")
}
Text("The image below should be stretched to touch the left and right window edge, without being cropped by the Form.")
Image(systemName: "sun.max.fill")
.resizable()
.aspectRatio(contentMode: .fill)
.listRowInsets(EdgeInsets()) // this is supposed to fix the problem, but all it does is to set the default item inner padding to zero, so the image at least touches the edge of teal area.
.listRowBackground(Color.teal)
Section(
header: Text("Last section")
) {
Text("Hello world")
}
}
}
}
PlaygroundPage.current.setLiveView(ContentView())
How it looks:
Unfortunately, SwiftUI Form is very temperamental and forces you to strictly adhere to the standard iOS Settings screen formatting.
Fortunately, you can re-implement similar formatting yourself with SwiftUI!
For the top, something like:
VStack(spacing: 4) {
Text("FIRST SECTION")
.font(.system(size: 12, weight: .regular))
.foregroundColor(.gray)
.padding(.leading)
Text("Hello, world!")
.font(.system(size: 15, weight: .regular))
.foregroundColor(.black)
.padding(.horizontal)
.frame(height: 44, alignment: .center)
.background(Color.white)
.cornerRadius(10)
}

SwiftUI: How to know correct order of modifiers?

I'm pretty new to SwiftUI, learning it for the first time, and couldn’t understand why the below snippet doesn’t work. Ideally, the VStack should stretch in all directions and the Image should have a width of 200px without losing its aspect ratio.
Code
struct ContentView: View {
var body: some View {
VStack() {
Image("Image Name")
.resizable()
.frame(width: 200)
.aspectRatio(contentMode: .fit)
}
.background(Color.red)
.frame(maxWidth: .infinity,maxHeight: .infinity)
}
}
After I accidentally reordered the modifiers, it worked. So, how am I supposed to know the correct order of modifiers without a hit and trial method each time?
// new VStack modifier order
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red)
// new Image modifier order
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200)
The best way to think about it for now is to imagine that SwiftUI renders your view after every single modifier. So, as soon as you say .background(Color.red) it colors the background in red, regardless of what frame you give it. If you then later expand the frame, it won’t magically redraw the background – that was already applied.
Of course, this isn’t actually how SwiftUI works, because if it did it would be a performance nightmare, but it’s a neat mental shortcut to use while you’re learning.
Please refer to this link for more details https://www.hackingwithswift.com/books/ios-swiftui/why-modifier-order-matters#:~:text=Every%20time%20we%20modify%20a,up%3A%20ModifiedContent%3CModifiedContent%3C%E2%80%A6

SwiftUI modal sheet dismisses itself after half a second

I have got a modal sheet, here is the code:
SettingsDashboardView:
#State private var notificationsSettingsSheet = false
var body: some View {
Button(action: {
self.notificationsSettingsSheet.toggle()
}) {
VStack(alignment: .leading) {
HStack(alignment: .top, spacing: 4) {
Label("Set Daily Reminders", systemImage: "alarm").foregroundColor(Color("TextColor"))
.font(.system(.headline, design: .rounded))
Spacer()
}
}
}
.sheet(isPresented: $notificationsSettingsSheet) {
NotificationSettingsModal()
}
}
NotificationSettingsModal:
var body: some View {
ZStack(alignment: .bottom) {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
Text("Daily Reminders")
.font(.system(.title, design: .rounded))
.fontWeight(.bold)
.padding(.top, headingTopPadding)
.padding(.horizontal, headingHorizontalPadding).foregroundColor(Color("TextColor"))
Spacer().frame(height: 164)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
Spacer().frame(height: 64)
}
}.background(Color("BackgroundColor").edgesIgnoringSafeArea(.all))
}
When I launch the app and open my sheet, in about 50% of cases sheet dismisses itself after about half a second. If I open sheet after that everything works fine. What can cause this problem?
This will probably not solve the mentioned issue but can be useful for others.
In most cases, this issue happens when the view gets redrawn due to a change in some variables. Be careful that it might be the parent view that have some variables changes.
The best way to debug this kind of behaviour is to use the technique describe here, on Hacking with Swift. The idea is to identify what change caused a view to reload itself by printing print(Self._printChanges()) inside the body property. Note that by doing it, you will temporarily need to add an explicit return.
Then, observer the console and it most cases you will be able to identify the issue and refactor your code.
In my experience (does not seem to be the case here) this often happens when using #Environment(\.editMode) var editMode in both the view and parent view. For some reasons this value changes in both views when presenting a sheet, causing the view to be redrawn and the sheet closed.
I solved this problem by removing the codes below while setting to NavigationView on my homeView this week, which caused my subView's sheet automatically dismissed the first time showing.
NavigationView {...}
// .navigationViewStyle(StackNavigationViewStyle())

Add View in navigationBarTitle - SwiftUI

I am able to add Title using .navigationBarTitle(Text((msgDetails.name))) But i wanted to add subtitle under the title in the navigationbar. Looks like title will not accept the View and it accepts only Text. I tried \n in the title but it is not working. IS there any way i can add the subtitle in navigation bar. I used leading and trailing to add left and right button in the navigation bar. I wanted to show title and subtitle along with this left and right button
Navigation Bar
If you look in SwiftUI documentation you'll see only a few overloads of navigationBarTitle function. All of them requires special parameters, like Text or StringProtocol. So you can't just put some View into navigation bar.
I can propose one strange, but working version. It's about using .navigationBarItems(leading:... - it requires some view, which you can customize (within reason). Here is simple example:
struct ContentView: View {
var body: some View {
NavigationView {
Text("Main view")
.navigationBarItems(leading:
HStack {
Button(action: {}) {
Image(systemName: "return")
}
VStack {
Text("Title")
.bold()
.font(.system(size: 30))
Text("Subtitle")
.italic()
.font(.system(size: 15))
}
.padding(.horizontal, 100) // mb it's better to use GeometryReader for centering
})
}
}
}
and you'll achieve something like this:

How to show a view when tapping on a list item?

I am working with a list in SwiftUI, I am attempting to recreate a system I had with TableView whereby a user can tap a cell and then a new view is presented with data relating to said cell. Now we have lists my code has changed to the following:
List {
ForEach(clients, id: \.id) { client in
VStack(alignment: .center) {
HStack{
Text(client.firstName ?? "Unknown" + " ")
.font(.system(size: 17))
.foregroundColor(Color.init(hex: "47535B"))
.fontWeight(.medium)
.multilineTextAlignment(.leading)
.padding(.leading)
Text(client.lastName ?? "Unknown")
.font(.system(size: 17))
.foregroundColor(Color.init(hex: "47535B"))
.fontWeight(.medium)
.multilineTextAlignment(.leading)
Spacer()
}
}
.frame(height: 50.0)
.background(Color.init(hex: "F6F6F6"))
.cornerRadius(7.0)
}
}
.padding(.horizontal, 3.0)
.padding(.vertical, 115.0)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
I realise I can use a NavigationLink and place the entire thing into a NavigationView but this functionality is not what I want, below is an image of my interface. What I am trying to achieve is when the user taps a cell it presents the data in the space on the right where it says "Select a client to view their profile". With the NavigationView setup I can only use the 2 default styles neither of which are suitable for me since I cannot customise where the navigation view gets placed. Is there a way I can register the same tap but have my own custom system for displaying the resulting data where I want in my interface? Perhaps I am wrong about NavigationView or maybe there is a way to have the NavigationView be positioned entirely outside of the view that contains the item list?
If you don't want to or aren't using use a NavigationView, then you likely have both the client list and the client detail in the same view somewhere. I would try adding #State private var selectedClient: Client? = nil to whatever view has both the list and the detail.
First, pass selectedClient as a binding to the list. Next, whenever one of the list items is tapped (achievable through .onTapGesture() or Button), Update selectedClient.
In your detail view, accept a bindable Client? parameter. If it's nil, then just show your current Text view. If it's not nil, then build the detail UI.
Hope this helps!

Resources