I've run in to a strange behavior in SwiftUI that I can't seem to work around.
Given the following simple example app I experience this behavior: The toolbar item renders correctly on the initial run, but navigating away and returning it gets clipped.
Sample code to recreate this:
ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: View2()) {
Text("Hello, world!")
.padding()
.navigationTitle("View 1")
.toolbar {
Circle()
.fill(Color.red)
.frame(width: 150, height: 150, alignment: .center)
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
View2.swift
import SwiftUI
struct View2: View {
var body: some View {
Text(/*#START_MENU_TOKEN#*/"Hello, World!"/*#END_MENU_TOKEN#*/)
}
}
struct View2_Previews: PreviewProvider {
static var previews: some View {
View2()
}
}
It is clipped by navigation bar as seen on view debug below so it is just rendering issue (should be clipped always):
A possible solution is to use that widget (Circle in the case) above NavigationView and align it with toolbar item.
Here is main part:
.toolbar {
Color.clear
.frame(width: 150)
.overlay(GeometryReader {
Color.clear.preference(key: ViewPointKey.self,
value: [$0.frame(in: .global).center])
})
}
//...
Circle().fill(Color.red)
.frame(width: 150, height: 150)
.position(x: pos.x, y: pos.y) // << here !!
//...
.onPreferenceChange(ViewPointKey.self) {
pos = $0.first ?? .zero
}
Complete findings and code is here
Related
I am trying to implement list reordering, basic code example:
import SwiftUI
struct ContentView: View {
#State var items = [
"One",
"Two",
"Three"
]
var body: some View {
NavigationStack {
ZStack {
Color.gray
.ignoresSafeArea()
List($items, id: \.self, editActions: .move) { $item in
ListItem(text: item)
}
.scrollContentBackground(.hidden)
.listStyle(.plain)
}
.navigationTitle("Test")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct ListItem: View {
let text: String
var body: some View {
Text(text)
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.padding()
.background(.blue)
.clipShape(RoundedRectangle(cornerRadius: 4.0))
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
It all works fine and well however when I long press to begin the drag gesture the background for the list item appears black. I have added all the modifiers such as listRowBackground and scrollContentBackground but the problem persists. Any ideas what I'm doing wrong here?
Use your page background color (gray) for your listRowBackground modifier.
Text(text)
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.padding()
.background(.blue)
.clipShape(RoundedRectangle(cornerRadius: 4.0))
.listRowSeparator(.hidden)
.listRowBackground(Color.gray)
I'm trying to show my onboarding view as a sheet. Is there anyone here who can help me?
Here's how it looks right now:
I want it to be displayed as a sheet from top to bottom. I tried to do it, but it doesn't work. Is there a solution to this?
Here's the code:
Login view
import SwiftUI
struct LoginView: View {
#AppStorage("needsAppOnboarding") private var needsAppOnboarding: Bool = false
var body: some View {
Text(/*#START_MENU_TOKEN#*/"Hello, World!"/*#END_MENU_TOKEN#*/)
.sheet(isPresented: $needsAppOnboarding) {
OnboardingView()
}
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView()
}
}
OnboardingView
import SwiftUI
struct OnboardingView: View {
var body: some View {
VStack(spacing: 20) {
Spacer()
Image("wulkanowy-svg")
.resizable()
.frame(width: 200, height: 200, alignment: .top)
.foregroundColor(Color("OnboardingColor"))
VStack(spacing: 20) {
Text("onboarding.description.title")
.font(.headline)
.multilineTextAlignment(.center)
Text("onboarding.description.content")
.font(.subheadline)
.multilineTextAlignment(.center)
}
.padding(.horizontal, 20)
Spacer()
OnboardingButtonView()
.padding()
}
}
}
struct WulkanowyCardView_Previews: PreviewProvider {
static var previews: some View {
Group {
OnboardingView().previewLayout(.fixed(width: 320, height: 640))
}
}
}
OnboardingButtonView
import SwiftUI
struct OnboardingButtonView: View {
#AppStorage("needsAppOnboarding") var needsAppOnboarding: Bool = true
var body: some View {
Button(action: {
needsAppOnboarding = false
}, label: {
Text("onboarding.continue")
})
.padding(10)
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color("OnboardingColor"))
.foregroundColor(.white)
.font(.title)
.cornerRadius(20)
}
}
struct OnboardingButtonView_Previews: PreviewProvider {
static var previews: some View {
OnboardingButtonView()
.previewLayout(.sizeThatFits)
}
}
I'm trying to make this Profile Button to work inside a ScrollView, that when tapped, a modal view should come up. But for some reason, when I add some offset to the Button, to make it inline with the Navigation Title, the Button stops working.
Does anyone know a fix for this?
Here's my code:
import SwiftUI
struct TestView: View {
#State var showMenu = false
var body: some View {
NavigationView {
ScrollView {
VStack {
HStack {
Spacer()
Image(systemName: "person.crop.circle")
.font(.system(size: 36))
.foregroundColor(.blue)
.frame(width: 44, height: 44)
.onTapGesture {
showMenu.toggle()
}
.offset(y: -64)
}
Spacer()
}
.padding()
.sheet(isPresented: $showMenu) {
MenuView()
}
}
.navigationTitle("Browse")
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
I have a HomeView which is basically a ZStack of a MapView and a Circle, and I am following https://www.hackingwithswift.com/books/ios-swiftui/advanced-mkmapview-with-swiftui.
Both the map and circle appear when I am using a ZStack right here in the ContentView. However, I have authentication set up so when I do the following (below), I only see the map and not the circle:
ContentView.swift
import SwiftUI
struct ContentView: View {
#EnvironmentObject var auth: AuthenticationState
var body: some View {
ZStack {
if auth.user != nil {
HomeView()
} else {
GatekeeperView()
}
}
}
}
HomeView.swift
struct HomeView: View {
var body: some View {
ZStack {
Circle()
.fill(Color.blue)
.opacity(1)
.frame(width: 32, height: 32)
MapView().edgesIgnoringSafeArea(.all)
}
}
}
Authenticated users get to see the HomeView. Unfortunately, the circle does not show up.
Try explicitly setting .zIndex() for your views in ZStack:
struct HomeView: View {
var body: some View {
ZStack {
Circle()
.fill(Color.blue)
.opacity(1)
.frame(width: 32, height: 32)
.zIndex(1)
MapView().edgesIgnoringSafeArea(.all)
.zIndex(2)
}
}
}
Alternatively reorder your ZStack:
struct HomeView: View {
var body: some View {
ZStack {
MapView().edgesIgnoringSafeArea(.all)
Circle()
.fill(Color.blue)
.opacity(1)
.frame(width: 32, height: 32)
}
}
}
Just can't get my head around this. I have a simple animation which works perfectly. But when I wrap the view into a ScrollView (by uncommenting the 2 lines) it does not animate anymore? Anybody a clue?
import SwiftUI
struct ContentView: View {
#State var offset = CGSize(width: 0, height: 0)
var body: some View {
// ScrollView {
VStack(spacing: 50) {
Rectangle()
.frame(width: 100, height: 100)
.offset(self.offset)
Button(action: {
withAnimation {
self.offset.width += 66
}
})
{
Text("Animate me")
}
}.frame(width: 300)
// }
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I've already noticed this behaviour. The issue seems to be the explicit animation. If you go for an implicit animation it works:
struct ContentView: View {
#State var offset = CGSize(width: 0, height: 0)
var body: some View {
ScrollView {
VStack(spacing: 50) {
Rectangle()
.frame(width: 100, height: 100)
.offset(self.offset)
.animation(.linear)
Button(action: {
self.offset.width += 66
})
{
Text("Animate me")
}
}.frame(width: 300)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I haven't managed to get the reason yet, so consider this as a workaround. It may be a SwiftUI bug or something I still can't understand.