First attempt at SwiftUI here, converting a Swift Macos app to SwiftUI.
I'm wondering if it is possible to have the entire area within a Buttons frame be clickable as a part of the button. Seems only the highlighted area around the text is a part of the button.
Buttons
Code for one of the calculator buttons:
Button("÷", action: {
self.calculatorVM.operate("÷")
})
.frame(width: self.calculatorVM.buttonWidth, height: self.calculatorVM.buttonHeight)
.border(Color.blue)
.font(.body)
Got it squared away by configuring the button as follows:
Button(action: {
self.calculatorVM.recieveInput(self.button)
}) {
Text(button.title)
.font(.body)
.frame(width: buttonWidth, height: 50)
.background(button.buttonHeight)
}
.buttonStyle(PlainButtonStyle())
Related
I have a very simple view that only shows a Text, a Shape, and a Button stacked vertically in a ScrollView. The Shape is a Capsule and is conditionally shown only when showCapsule is true.
struct ContentView: View {
#State var showCapsule = true
var body: some View {
ScrollView {
VStack(spacing: 16) {
Text("Why, oh why? 😩")
.font(.headline)
if showCapsule {
Capsule()
.frame(maxWidth: .infinity)
.frame(height: 100)
.foregroundColor(.blue)
}
Button {
showCapsule.toggle()
} label: {
Text(showCapsule ? "Hide" : "Show")
}
.buttonStyle(.bordered)
}
.frame(maxWidth: .infinity)
.padding()
}
.animation(.default, value: showCapsule)
}
}
Observed and expected animation
I want to animate the appearance and disappearance of the Capsule, but the result is totally not what I want. While the Capsule fades out (which is okay), the button is animated in two different ways simultaneously:
Its background shape (the grey rounded rectangle) move from the old to its new position.
Its text fades out at its old position and fades in at its new position.
Of course, (2) is not what I want. Instead, I want the button to move as a unit: The entire thing should move from its old to its new position while the text is faded inside of it.
The broader picture
This is a minimal example for a broader question: How do I animate changes to a view that is semantically the same but value-wise different?
In the Button initializer, I use a ternary operator to conditionally pass a different string to its Text label:
Button {
showCapsule.toggle()
} label: {
Text(showCapsule ? "Hide" : "Show")
}
Text("Hide") is a different value than Text("Show"), so I guess that's why SwiftUI can't identify them and doesn't "understand" that it should animate them in place. I observed the same behavior with custom views with let constants. Is there a way to make SwiftUI treat such views – and especially this Button – as a unit and animate them correctly?
Note
I'm not looking for a workaround like using two Text fields (or Buttons) and show/hide them by setting their opacity accordingly. Rather looking for a general solution for this kind of problem that solves the identity problem rather than patching its symptoms.
You can fix this by adding .drawingGroup modifier to your button:
Button(showCapsule ? "Hide" : "Show") {
showCapsule.toggle()
}
.drawingGroup()
.buttonStyle(.bordered)
Alternatively, you could have two buttons that are shown and hidden:
ZStack {
Button("Hide") {
showCapsule.toggle()
}
.buttonStyle(.bordered)
.opacity(showCapsule ? 1 : 0)
Button("Show in a very long button") {
showCapsule.toggle()
}
.buttonStyle(.bordered)
.opacity(showCapsule ? 0 : 1)
}
Fixed sized SwiftUI windows with toolbar have an extra band in full-screen mode. This seems like a bug. If a tab bar is also added (by pressing Command+N in fullscreen mode) the extra band doubles. How to get rid of these bands?
struct ContentView: View {
var body: some View {
Circle()
.toolbar {
Button(action:{}) {
Label("Favorites", systemImage: "heart")
}
}
.frame(width: 400, height: 30)
}
}
I'm developing MacOS App with SwiftUI.
Let's say we have a fixed size window, which will show a scrollable image.
Now I want to create a feature which can let user to scale the image, and can scroll and view the scaled image inside the window.
The problem is, once I scaled up the image, the scroll area seems not expended together, and the scroll area is not big enough to reach each corner of the image.
struct ContentView: View {
var body: some View {
ScrollView([.horizontal, .vertical] , showsIndicators: true ){
Image("test")
.scaleEffect(2)
}
.frame(width: 500, height: 500)
}
}
I have tried to set the Image's frame by using GeometryReader, but got the same result.
MacOS 11.1, Xcode 12.3
Thanks!
.scaleEffect seems to perform a visual transform on the view without actually affecting its frame, so the ScrollView doesn't know to accommodate the bigger size.
.resizable() and .frame on the image seem to do the trick:
struct ContentView: View {
#State private var scale : CGFloat = 1.0
var body: some View {
VStack {
ScrollView([.horizontal, .vertical] , showsIndicators: true ){
Image(systemName: "plus.circle")
.resizable()
.frame(width: 300 * scale,height: 300 * scale)
}
.frame(width: 500, height: 500)
Slider(value: $scale, in: (0...5))
}
}
}
I am trying to increase the touchable area of a button inside a NavigationView. It does not work even though the area is made bigger. My code is below:
var body: some View {
NavigationView {
List(taskStore.tasks) { tasks in
Text(tasks.name)
}
.navigationBarTitle("Tasks")
.navigationBarItems(
trailing: Button(action: {
self.modalIsPresented = true
}){
Image(systemName: "plus")
.frame(width: 200, height: 200)
.contentShape(Rectangle())
.background(Color.yellow)
})}
The green area is touchable and the red area isn't touchable.
I found a solution online that works. However this solution only works for a button that is NOT in the NavigationView. So if I put the button in "some view" like the following below, it works as per the solution:
var body: some View {
Button(action: {self.modalIsPresented = true} ) {
Text("Default padding")
.padding(50)
.background(Color.yellow)
}}}
But when I put the button in a Navigation View like my code, the yellow area is not touchable. How can I get the whole yellow area (red box) to be touchable like the solution?
Thanks :D
Example of solution:
If you want a button in the navigation bar, it is only going to be clickable inside the navigation bar, no matter what you try to set the image's frame at, and the NavigationView determines that height, no matter what the children- the button, in this case- may want.
Historically, changing the height of the NavigationBar has not been supported: see the comments here
Now you could do something funky with ZStacks- put a button on top of the navigation view, perhaps- but you're not going to be able to put anything larger than the set height inside the navigation bar.
I think you may find your desired effect inside the .navigationBarItems(trailing:) if you use .contentShape(Rectangle()) modifier. Or you could use other shapes to suit your needs. Then adjust the size of the .frame to adjust the tappable area as desired. Here is a quick code example.
struct ContentView: View {
var body: some View {
NavigationView {
List {
Text("Hello, World")
}
.navigationBarTitle(Text("Items"))
.navigationBarItems(trailing: Button(action: {
print("Do Something")
}, label: {
Image(systemName: "plus")
.frame(width: 100, height: 50)
.contentShape(Rectangle())
.border(Color.red, width: 3)
.background(Color.gray)
}))
}
}
}
I hope this helps.
I am having an issues in swiftui when creating a button or navigationlink using an image as the button. Once I get the code setup the image is blue.
NavigationView {
VStack {
Text("Hello World")
NavigationLink(destination: areaexample()) {
Image("testpic")
.resizable()
.frame(width: 200, height: 200)
.cornerRadius(50)
}
}
}
Some components in iOS automatically apply a caller called tint. If you need to override this behavior, Set the rendering mode of the image to .renderingMode(.original)
Image("testpic").renderingMode(.original)