I plan to have a pop up view that blur out all the underlying UI behind it after it show, however my background "RoundedRectangle(cornerRadius: 20)
.background(
Color.white.opacity(0.7)
)" did not show a transparent white or blur like I planned.
var body: some View {
ZStack() {
NavigationStack {
ZStack() {
MyBackGround()
VStack() {
TextField("username", text: $user)
.padding()
.background(.brown)
SecureField("password", text: $pw)
.padding()
.background(.brown)
Button("CONFIRM") {
popUp.toggle()
}
.padding()
.cornerRadius(20)
.padding()
}
}
}
if popUp {
RoundedRectangle(cornerRadius: 20)
.background(
Color.white.opacity(0.7)
)
MyPopUP()
}
}
}
Your popup background is black because by default RoundedRectangle is filled with black color, so instead of adding background modifier we need to fill it with that color.
NavigationStack {
// content ...
}
.blur(radius: popUp ? 5 : 0) // to blur (constant is up to you)
.disabled(popUp) // bluring does not prevent interaction
if popUp {
// if semi-transparent background is still needed
RoundedRectangle(cornerRadius: 20)
.foregroundColor(Color.white.opacity(0.7)) // << here !!
MyPopUP()
}
I suggest using the provided SwiftUI .blur() for this context instead.
Add this line of code after your NavigationStack.
NavigationStack{...}
.blur(radius: popUp ? 10 : 0)
if popUp {
MyPopUP()
}
Related
I am trying to make the SwiftUI Form translucent. I've tried applying .background(.thinMaterial) modifier to the Form. However, this changes the look of the scroll view background. I'd like to apply translucency to the area that is white in the picture I attached.
Is there a way to do it? I am developing for iOS 16.
var body: some View {
NavigationStack(path: $path) {
ZStack {
LinearGradient(gradient: Gradient(colors: [.pink, .yellow]),
startPoint: .topTrailing,
endPoint: .bottomLeading)
.edgesIgnoringSafeArea(.all)
Form {
VStack {
...
}
}.scrollContentBackground(.hidden)
}
}
It seems Form is just a List under the hood. So by applying .scrollContentBackground(.hidden) you are clearing the List background.
By using .background on Form you are setting the background for the entire List again. To set the background only on the implicit Section you need another modifier. .listRowBackground.
But listRowBackground has this signature:
func listRowBackground<V>(_ view: V?) -> some View where V : View
So you can´t use .thinMaterial. But you can add a new background to the VStack.
A possible solution would be:
var body: some View {
NavigationStack(path: $path) {
ZStack {
LinearGradient(gradient: Gradient(colors: [.pink, .yellow]),
startPoint: .topTrailing,
endPoint: .bottomLeading)
.edgesIgnoringSafeArea(.all)
Form {
VStack {
TextField("", text: $text)
Button("test"){
}
.buttonStyle(.borderedProminent)
Button("test"){
}.buttonStyle(.borderedProminent)
}
// this will clear the background
.listRowBackground(Color.clear)
// add some padding around the VStack
.padding()
// apply a new background
.background(.ultraThinMaterial)
// make the edges round again
.mask {
RoundedRectangle(cornerRadius: 20)
}
}
.scrollContentBackground(.hidden)
}
}
}
Result:
Change the form's opacity to bleed through the background colour/image:
Form {
/...
}
.opacity(0.5). // 0 = fully translucent ... 1 = opaque
I have a question about a part of my code in my custom button style. So I created a custom button style and this is what I am returning:
struct CustomStyle: ButtonStyle{
func makeBody(configuration: Configuration) -> some View {
return AnyView(Label {
configuration.label
} icon: {
Image(systemName: "plus").resizable().frame(width: 30, height: 30)
})
.foregroundColor(
configuration.isPressed ? Color.blue : Color.red
)
.padding()
.frame(maxWidth: .infinity, minHeight: 20)
.background(Color.yellow)
.clipShape(Capsule())
}
}
struct ContentView: View {
var body: some View {
VStack {
Button(action: {}, label: { Text("amin") })
.buttonStyle(CustomStyle())
}
}
}
The foregroundColor should change when I tap the button, and it does change. The issue is, the icon takes a few more milliseconds to go back to its original color. For example, let's say the color of the text and icon is red. When I click on the button both become blue, but text goes back to red immediately once I let go and icon(image) goes back to red with a very brief(a few millisecond) animation. I want both to be synced.
notes:
I know that most of the time in button styles we just return configuration.label, but what I am returning also works and has no issues. I have to do this because I want my button to have an image next to its text.
icon in this case is Image(systemName: "plus")
It is possible to fix by just disabling animation (tested with Xcode 13.4 / iOS 15.5)
struct CustomStyle: ButtonStyle{
func makeBody(configuration: Configuration) -> some View {
Label {
configuration.label
} icon: {
Image(systemName: "plus").resizable().frame(width: 30, height: 30)
}
.animation(.none, value: configuration.isPressed) // << here !!
.foregroundColor(
configuration.isPressed ? Color.blue : Color.red
)
I created SwiftUI Button and its touch area is slightly strange. The touch area extends the label of the button.
This is my code.
struct TestView: View {
var body: some View {
HStack(spacing: 0) {
Spacer()
Button {
print("aaaaaa")
} label: {
HStack(spacing: 0) {
Spacer()
}
.frame(width: 50.0, height: 50.0)
.background(Color.yellow)
}
Spacer()
}
.frame(height: 50.0)
.background(Color.red)
}
}
And I'll attach the result in simulator.
The button area is filled with yellow color. But I could click the button outside of yellow color.
The same thing happens in real device too.
How is this possible?
This is a completely normal behavior of Button/onTapGesture in SwiftUI. Its touch area is slightly bigger than its border size.
This is not a bug or glitch.
I’m trying to add an accessory view embedded in a navigation bar below the title, which can be seen in the default iOS calendar app (the “s m t w t f s” row) or the GitHub mobile app:
And I’d like it to work along with the large title style navigation bar like the GH mobile.
LazyVStack’s pinnedView with a section header almost work, but I can’t get the background color to make it seemless with the navigation bar, even with the ultraThinMaterial. It also leaves the divider line between the pinned view and the bar.
Is there a way to achieve this layout?
Solutions in SwiftUI, SwiftUI+Introspect, and UIKit are all welcome!
Have you tried setting a .safeAreaInset view? This will have the stickiness you're looking for, and items in the "main" part of the view will take its height into account when rendering, so won't get obscured.
Here's a quick example I knocked up:
struct ContentView: View {
var body: some View {
NavigationView {
List {
ForEach(0 ..< 30) { item in
Text("Hello, world!")
}
}
.navigationTitle("Accessory View")
.safeAreaInset(edge: .top) {
AccessoryView()
}
}
}
}
struct AccessoryView: View {
var body: some View {
HStack {
Button("Button") { }
Button("Button") { }
Button("Button") { }
Spacer()
}
.padding()
.background(Color(uiColor: .systemGroupedBackground))
.buttonStyle(.bordered)
.controlSize(.mini)
}
}
You have to give the view a background otherwise it'll be transparent – but that background will (as long as it's a colour or a material) automatically extend into the navigation bar itself. Here's a GIF of the above code in action, where I've set the background to match the grouped list's background:
It's not perfect, especially as it looks distinct from the nav bar on scroll, but it might be useable for you?
Another idea is to replace the navigation bar with a custom one like this:
{
...
}
.safeAreaInset(edge: .top) {
VStack(alignment: .leading, spacing: 8) {
HStack() {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "chevron.backward")
}
Spacer()
Text(navigationTitle).font(.title2).bold()
.multilineTextAlignment(.center)
.foregroundColor(.accentColor)
.frame(maxWidth: .infinity)
Spacer()
}
HStack {
Button("Button") { }
Button("Button") { }
Button("Button") { }
Spacer()
}
}
.padding()
.background(
.bar
)
}
You will also have to set:
.navigationBarBackButtonHidden(true)
and do not set a navigation title:
// .navigationTitle("....")
In this sample app, I have a title in the top left of the screen, and a button in the bottom right. I'm using stacks and spacers to align them.
Currently, when you press the button, it animates up/left a little. But I want the button to animate to the exact center of the screen (or safe area), regardless of device or button size. The code is shown below, along with images of the start and end of the animation I want.
struct ContentView: View {
#State var buttonIsMoved = false
var body: some View {
VStack {
HStack {
Text("Title")
.font(.largeTitle)
Spacer()
}
Spacer()
HStack {
Spacer()
// This is the button I want to animate to the center
Button(action: {
self.buttonIsMoved.toggle()
}) {
Text("This is a button")
.foregroundColor(.black)
.padding(16)
.background(Color.green)
}
// Currently I'm just using fixed offset values,
// but I want it to move to the actual center of the screen
.offset(buttonIsMoved ? CGSize(width: -50, height: -200) : .zero)
.animation(.easeInOut)
}
}
.padding(32)
}
}
Start of animation
End of animation
If I use .offset(), I don't know how to calculate the distance between the button's center and the center of the screen. I've also tried to use .position() but it's based on the parent view, which in this case is an HStack below the title, so it wouldn't be centered within the whole screen. I've also heard of GeometryReader, but I can't figure out how to use it for this purpose.
Here is possible solution - no hardcoding, based on SwiftUI native layout engine.
Tested with Xcode 11.4 / iOS 13.4
struct DemoAnimateLayout: View {
#State var buttonIsMoved = false
var body: some View {
ZStack {
VStack {
HStack {
Text("Title")
.font(.largeTitle)
Spacer()
}
Spacer()
}
VStack {
if !buttonIsMoved { // << here !!
Spacer()
}
HStack {
if !buttonIsMoved { // << here !!
Spacer()
}
// This is the button I want to animate to the center
Button(action: {
self.buttonIsMoved.toggle()
}) {
Text("This is a button")
.foregroundColor(.black)
.padding(16)
.background(Color.green)
}
}
}
.animation(.easeInOut) // << animate container layout !!
}.padding(32)
}
}