swiftui list not working when put into ZStack - list

I use ZStack to combine a list and Color, after doing it, List will not scroll and there's no output when clicking the text.
Does anyone know how to fix it?
Thanks
struct ContentView: View {
var body: some View {
ZStack{
List{
ForEach(1...30, id: \.self){ i in
Text("ROW \(i)")
.font(.system(size: 40))
.onTapGesture {
print("clicked \(i)")
}
}
}
Color.black.opacity(0.2)
}
}
}

Move Color before List and it will work. See the altered code below.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack{
Color.black.opacity(0.2)
List{
ForEach(1...30, id: \.self) { i in
Text("ROW \(i)")
.font(.system(size: 40))
.onTapGesture {
print("clicked \(i)")
}
}
}
}
}
}

I don't know why it isn't working — probably a bug — but you can (and probably should) do this instead.
struct ContentView: View {
var body: some View {
List{
ForEach(1...30, id: \.self){ i in
Text("ROW \(i)")
.font(.system(size: 40))
.onTapGesture {
print("clicked \(i)")
}
}
}.background(Color.black.opacity(0.2))
}
}

Related

How to add the Navigation Bar space in SwiftUI NavigationView

I hide the navigation bar and provide a customized navigation bar.
But my list appears below the navigation bar.
I want the list to appear below the navigation bar when scrolling.
My desired effect:
Actual effect: Blocked 0
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
TabView {
FirstView()
.tabItem {
Image(systemName: "folder.fill")
Text("Home")
}
SecondView()
.tabItem {
Image(systemName: "folder.fill")
Text("SecondView")
}
}
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct FirstView: View {
var body: some View {
ZStack(alignment: .top) {
List {
ForEach(0..<40, id: \.self) { index in
Text("cell view \(index)")
}
}
.listStyle(.inset)
HStack {
Text("首页")
}
.frame(maxWidth: .infinity)
.frame(height: 44)
.background(.bar)
}
.navigationBarTitleDisplayMode(.inline)
//.ignoresSafeArea(edges: .)
}
}
struct SecondView: View {
var body: some View {
List {
ForEach(0..<40, id: \.self) { index in
Text("FirstView2")
}
}
.listStyle(.inset)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Please do not add padding to the List. This is a security zone problem.
Edit: scrolling under the header
For a glass effect under the header:
You can add a "dummy" element that will push the list one row down. So the the header will cover the dummy element and the whole list will be visible.
struct FirstView: View {
var body: some View {
ZStack(alignment: .top) {
List {
// Dummy Text
Text("")
ForEach(0..<40, id: \.self) { index in
Text("cell view \(index)")
}
}
.listStyle(.inset)
HStack {
Text("首页")
}
.frame(maxWidth: .infinity)
.frame(height: 44)
.background(.bar)
.opacity(0.7)
}
.navigationBarTitleDisplayMode(.inline)
//.ignoresSafeArea(edges: .)
}
}
Original answer
Your FirstView is using a ZStack, which tells exactly the compiler to show one view on top of (covering) the other. Use a VStack, that should solve your issue.
struct FirstView: View {
var body: some View {
VStack {
HStack {
Text("首页")
}
.frame(maxWidth: .infinity)
.frame(height: 44)
.background(.bar)
List {
ForEach(0..<40, id: \.self) { index in
Text("cell view \(index)")
}
}
.listStyle(.inset)
}
.navigationBarTitleDisplayMode(.inline)
//.ignoresSafeArea(edges: .)
}
}

SwiftUI Elements move down between views

I have two views
import SwiftUI
import CoreData
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Image("qr-code")
.resizable()
.scaledToFit()
.position(x: 100, y: 100)
.offset(x: 100)
Text("Thank you")
.position(x: 200)
}.toolbar{
ToolbarItemGroup(placement: .bottomBar) {
NavigationLink(destination: ContentView().navigationBarBackButtonHidden(true)) {
Text("Show QR")
}
Spacer()
NavigationLink(destination: CustomizeView().navigationBarBackButtonHidden(true)) {
Text("Customize")
}
}
}
}
}
}
struct CustomizeView: View {
var body: some View {
List {
Section(header: Text("Important tasks")) {
Text("Task data goes here")
Text("Task data goes here")
}
}.toolbar{
ToolbarItemGroup(placement: .bottomBar) {
NavigationLink(destination: ContentView().navigationBarBackButtonHidden(true)) {
Text("Show QR")
}
Spacer()
NavigationLink(destination: CustomizeView().navigationBarBackButtonHidden(true)) {
Text("Customize")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
CustomizeView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
}
When I click on Customize and the click on Show, I see that the picture moves down. Is it expected behavior? How can I make sure that all elements are in the same positions regardless of how much I clicked on nvaigation buttons?
You should only have one NavigationView in your view hierarchy.
Right now, there are NavigationViews in ContentView and in CustomizeView any time you navigate to either with a NavigationLink, it will add an additional navigation bar to the view, pushing down your content.
To fix this, your root view could just be the NavigationView and then you links could navigation to views that do not contain additional NavigationViews.
struct ContentView: View {
var body: some View {
NavigationView {
BasicView()
}
}
}
struct BasicView : View {
var body: some View {
VStack {
Image(systemName: "pencil")
.resizable()
.scaledToFit()
.position(x: 100, y: 100)
.offset(x: 100)
Text("Thank you")
.position(x: 200)
}.toolbar{
ToolbarItemGroup(placement: .bottomBar) {
NavigationLink(destination: BasicView().navigationBarBackButtonHidden(true)) {
Text("Show QR")
}
Spacer()
NavigationLink(destination: CustomizeView().navigationBarBackButtonHidden(true)) {
Text("Customize")
}
}
}
}
}
struct CustomizeView: View {
var body: some View {
List {
Section(header: Text("Important tasks")) {
Text("Task data goes here")
Text("Task data goes here")
}
}.toolbar{
ToolbarItemGroup(placement: .bottomBar) {
NavigationLink(destination: BasicView().navigationBarBackButtonHidden(true)) {
Text("Show QR")
}
Spacer()
NavigationLink(destination: CustomizeView().navigationBarBackButtonHidden(true)) {
Text("Customize")
}
}
}
}
}
The problem is, I think you have called another ContentView from a ContentView. That's why one more navigation bar is added and shifted
You can create another view called QRShowView and place the qr image there and you have already CustomizeView view . Add two buttons in ContentView like show or customized.
Then call the respective view when is clicked.

how to integrate functions in SwiftUI in order to create re-usable code with input dictionaries

I want to re-use the VStack code in SwiftUI:
var primary:[String:String] = ["Price":"price", "Grade":"Two", "Recovery":"Three"]
var secondary:[String:String] = ["Price":"price", "Grade":"Two", "Recovery":"Three"]
struct ContentView: View {
var body: some View {
VStack {
List{
ForEach(Array(primary), id: \.key) { key, value in
HStack {
Text(key)
.fontWeight(.light)
.padding()
Spacer()
Text(value)
.fontWeight(.light)
.padding()
}
}
}
}
The VStack works fine , but I now want to create a function or a ViewModifier or some sort in order to run the VStack twice for both arrays. I could just put the code within the loop into a ViewModeifier in this simple example, but this is not the point. I want the whole VStack to be repeatable with input variables.
Separate mentioned VStack into dedicated view and use it with input data, like below (tested with Xcode 12 / iOS 14)
struct ReuseContentView: View {
var body: some View {
VStack {
DictionaryView(data: primary)
DictionaryView(data: secondary)
}
}
}
struct DictionaryView: View {
let data: [String: String]
var body: some View {
VStack {
List{
ForEach(Array(primary), id: \.key) { key, value in
HStack {
Text(key)
.fontWeight(.light)
.padding()
Spacer()
Text(value)
.fontWeight(.light)
.padding()
}
}
}
}
}
}

SwiftUI Popover Size is not expanding to fit content

Here is my code
struct ContentView: View {
#State var showingPopover = false
var body: some View {
VStack {
Spacer()
Text("Hello World")
Spacer()
HStack {
Spacer()
Button {
self.showingPopover.toggle()
} label: {
Image(systemName: "plus.circle")
}
.popover(isPresented: $showingPopover) {
List(0..<100) { Text("\($0)") }
}.padding(30)
}
}
}
}
This should produce a really nice popover coming from the plus button. But all I get is a really squashed down popover.
Any idea what I am missing here? Is there a way to tell the popover to expand more (without specifying a size)?
You may use a ScrollView and ForEach instead of a List:
struct ContentView: View {
#State var showingPopover = false
var body: some View {
VStack {
Spacer()
Text("Hello World")
Spacer()
HStack {
Spacer()
Button(action: {
self.showingPopover.toggle()
}) {
Image(systemName: "plus.circle")
}
.padding(30)
}
}
// can be attached to the button as well (as in the question)
.popover(isPresented: $showingPopover,
attachmentAnchor: .point(.bottomTrailing),
arrowEdge: .bottom) {
ScrollView(.vertical, showsIndicators: false) {
ForEach(0 ..< 100) {
Text("\($0)")
}
}
}
}
}
You can provide a custom frame for the List. Also, don't forget to embed List inside a ScrollView if you want it to scroll.
ScrollView {
List(0..<100) {
Text("\($0)")
}
.frame(width: 100, height: 250)
}

SwiftUI how to set image for NavigationBar titleView like in UIKit?

I want to set an image in the titleView of NavigationBar in SwiftUI, as we do in UIKit
navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
this is how we do it in UIKit.
anyone know how to do it?
Here's how to do it:
Add SwiftUIX to your project.
Set your custom title view via View.navigationBarTitleView(_:displayMode:)
Example code:
struct ContentView: View {
public var body: some View {
NavigationView {
Text("Hello World")
.navigationBarTitleView(MyView())
}
}
}
Simple, Just add your root view into ZStack with top alignment and add your custom center view after root view
struct CenterNavigattionBar: View {
var body: some View {
ZStack(alignment: .top){
//Root view with empty Title
NavigationView {
Text("Test Navigation")
.navigationBarTitle("",displayMode: .inline)
.navigationBarItems(leading: Text("Cancle"), trailing: Text("Done"))
}
//Your Custom Title
VStack{
Text("add title and")
.font(.headline)
Text("subtitle here")
.font(.subheadline)
}
}
}
}
Before Image
After Image
Just use a toolbar.
You can add any views
import SwiftUI
struct HomeView: View {
// MARK: - Initializer
init() {
let appearance = UINavigationBar.appearance()
appearance.isOpaque = true
appearance.isTranslucent = false
appearance.barTintColor = UIColor(named: "background")
appearance.shadowImage = UIImage()
}
// MARK: - View
// MARK: Public
var body: some View {
NavigationView {
VStack(spacing: 20) {
Text("Hello")
Text("Navigation Bar Test")
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading: leadingBarButtonItems, trailing: trailingBarButtonItems)
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text("Title").font(.headline)
Text("Subtitle").font(.subheadline)
}
}
}
}
}
// MARK: Private
private var leadingBarButtonItems: some View {
Button(action: {
}) {
Text("Left Button")
.font(.system(size: 12, weight: .medium))
}
}
private var trailingBarButtonItems: some View {
HStack {
Button(action: {
}) {
Text("R1\nButton")
.font(.system(size: 12, weight: .medium))
.multilineTextAlignment(.center)
}
Button(action: {
}) {
Text("R2\nButton")
.font(.system(size: 12, weight: .medium))
.multilineTextAlignment(.center)
}
}
}
}
Currently, you can't.
There are two overloads for .navigationBarTitle(), taking either a Text view or a type conforming to StringProtocol. You can't even pass in a modified view like Text("Title").font(.body). This would be a great feature, I'd submit a feature request: http://feedbackassistant.apple.com
Maybe this works for you?
Basically:
Use GeometryReader to get the width of the screen
Have NavigationBarItems(leading: HStack {Spacer() Image("name").resizable().frame(width:..., height: ..., alignment: .center Spacer()}.frame(width:geometry.size.width)
Example code:
struct ContentView: View {
var body: some View {
NavigationView {
GeometryReader { geometry in
Text("Hello, world!")
.padding()
.navigationTitle("test")
.navigationBarItems(leading: HStack {
Spacer()
Image("money")
.resizable()
.frame(width: 50, height: 50, alignment: .center)
Spacer()
}
.frame(width: geometry.size.width)
)
}
}
}
}
Try this...
How to put a logo in NavigationView in swiftui?
This shows how to handle adding an Image to NavigationView in SwiftUI. Hope it helps.