SwiftUI Set Image Boundary - swift

i want to set my right image frame boundaries like the left one without moving image or text. How can I do that. I tried a couple way but I couldn't find the solution.

You should use the built-in TabView. It provides all the functionality for you, and already made with accessibility in mind.
Here is an example (you can change it for your text and images):
struct ContentView: View {
#State private var selection: Int = 1
var body: some View {
TabView(selection: $selection) {
Text("Tab Content 1")
.tabItem {
Label("1", systemImage: "1.square")
}
.tag(1)
Text("Tab Content 2")
.tabItem {
Label("2", systemImage: "2.square")
}
.tag(2)
Text("Tab Content 3")
.tabItem {
Label("3", systemImage: "3.square")
}
.tag(3)
}
}
}
Result:
Custom version (highly unrecommended):
struct ContentView: View {
var body: some View {
VStack {
Spacer()
Text("Main Content")
Spacer()
HStack {
VStack {
Button {
//
} label: {
Label("1", systemImage: "1.square")
}
}.frame(maxWidth: .infinity)
VStack {
Button {
//
} label: {
Label("2", systemImage: "2.square")
}
}.frame(maxWidth: .infinity)
VStack {
Button {
//
} label: {
Label("3", systemImage: "3.square")
}
}.frame(maxWidth: .infinity)
}.frame(height: 50)
}
}
}

Related

How to prevent SwiftUI to rerender the entire tabview when using a Custom Alert view on top of it?

I have a tab view with 5 tabs. An alert view will be shown at the top when my view model's property gets true? But every time the alert is shown, the entire tab view is redrawn causing high cpu usage.
I want the alert to be shown regardless of which tab the user is in so I did a ZStack on the tab view to show the alert. But how do I prevent SwiftUI to redraw the entire tab view?
struct ContentView: View {
var body: some View {
ZStack {
TabView() {
MiniPlayer(content:
BrowseView()
)
.tabItem {
Group {
Image(systemName: "square.grid.2x2")
Text("Browse")
}
.onTapGesture {
selectedTab = 0
}
}
.tag(0)
MiniPlayer(content:
AllSongsListView()
)
.tabItem {
Group {
Image(systemName: "music.note")
Text("Songs")
}
.onTapGesture {
selectedTab = 1
}
}
.tag(1)
MiniPlayer(content:
PlaylistsView()
)
.tabItem {
Group {
Image(systemName: "music.note.list")
Text("Playlists")
}
.onTapGesture {
selectedTab = 2
}
}
.tag(2)
MiniPlayer(content:
MoreView()
)
.tabItem {
Group {
Image(systemName: "ellipsis")
Text("More")
}
.onTapGesture {
selectedTab = 3
}
}
.tag(3)
}
Alert()
}
.ignoresSafeArea(.keyboard)
}
}
struct Alert: View {
#EnvironmentObject var manager: Manager
var body: some View {
if manager.showSuccessAlert {
VStack {
VStack {
Text("Successful.")
.font(.system(size: 20, weight: .bold, design: .rounded))
.multilineTextAlignment(.center)
}
.frame(height: 50)
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.background(Color.blue)
.clipShape(Capsule())
Spacer()
}
.zIndex(1)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
withAnimation {
manager.showSuccessAlert = false
}
}
}
.transition(.asymmetric(insertion: .move(edge: .top), removal: .move(edge: .top)))
}
}
}
Try to move entire TabView into separated view, so that ZStack looks like
ZStack {
TabsContentView() // << move everything inside !!
Alert()
}

Swiftui invisible bar on the top of the screen

I have the following in problem.
If I try to give a view a color (Color.red for example)
I get the following output:
I can just add .edgesignoressafearea(.top) and the top also gets red. But when I want to add an clickable item, the user won't be able to click it since there still is this invisible bar on the top of the screen. Does Anyone know what my problem is? The problem is in all the tabable views(Timeline, Favorits, Discover, Account). So It must be in either the first code or in tabview (second code) that I send in this post.
When the user clicks on the app first they get this view sending the user to the signin view or the app itself:
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: tabView().navigationBarHidden(true), isActive: $tabview, label: { EmptyView() })
NavigationLink(destination: loginView().navigationBarHidden(true), isActive: $login, label: { EmptyView() })
if tabview == false && login == false {
Text("loading")
.onAppear(perform: checklogin)
}
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
Then the apps sends them to tabview:
var body: some View {
TabView(selection: $selection) {
Timeline()
.tabItem {
Label("timeline", systemImage: "house")
}
.tag(0)
Favorits()
.tabItem {
Label("favorits", systemImage: "heart")
}
.tag(1)
Discover()
.tabItem {
Label("discover", systemImage: "network")
}
.tag(2)
Account()
.tabItem {
Label("account", systemImage: "person")
}
.tag(3)
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
The problem happens on all of this views.
This is the view where I made the screenshot of:
var body: some View {
ZStack {
Color.red
Text("Hello fav!")
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
add .edgesIgnoringSafeArea(.top) and remove your navigation bar if you don't want to be able to get back to your login view anytime.
That's the full code with a login page, now it depends what you want on that login page :
import SwiftUI
struct ContentView: View {
#State var showLoginView: Bool = false
var body: some View {
VStack {
if showLoginView {
MainView()
} else {
Button("Login") {
self.showLoginView = true
}
}
}
}
}
struct MainView: View {
var body: some View {
TabView {
TimeLine()
.tabItem {
Label("timeline", systemImage: "house")
}
Favorits()
.tabItem {
Label("favorits", systemImage: "heart")
}
Discover()
.tabItem {
Label("discover", systemImage: "network")
}
Account()
.tabItem {
Label("account", systemImage: "person")
}
}
}
}
struct TimeLine: View {
var body: some View {
Color.blue
.edgesIgnoringSafeArea(.top)
}
}
struct Favorits: View {
var body: some View {
ZStack {
Color.red
.edgesIgnoringSafeArea(.top)
Text("Hello fav!")
}
}
}
struct Discover: View {
var body: some View {
Color.yellow
.edgesIgnoringSafeArea(.top)
}
}
struct Account: View {
var body: some View {
Color.purple
.edgesIgnoringSafeArea(.top)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You need to set the navigation Title to "" because your View is embedded in NavigationView
Create a ViewModifier like this and apply it to your VStack
struct HiddenNavBarModifier: ViewModifier {
func body(content: Content) -> some View {
content
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
}

SwiftUI navigationBarItems disappear in TabView

I have a view that has navigation bar items and I embed that view in a TabView. But when doing that, the bar items no longer appear. If I call the view outside of a TabView everything works as expected.
Below a small sample project to illustrate my issue, note that the TabView is not called on the initial ContentView but later down:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
NavigationLink(destination: WarehouseOrderTabView()){
Text("Click me")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct WarehouseOrderTabView: View {
var body: some View {
TabView{
TabView1().navigationBarTitle("Dashboard")
.tabItem {
Image(systemName: "gauge")
Text("Dashboard")
}
TabView2().navigationBarTitle("Orders")
.tabItem {
Image(systemName: "list.dash")
Text("Orders")
}
}
}
}
struct TabView1: View {
var body: some View {
Text("TabView 1")
//I would expect to see those bar items when displaying tab 1
.navigationBarItems(trailing: (
HStack{
Button(action: {
}, label: {
Image(systemName: "arrow.clockwise")
.font(.title)
})
.padding(.init(top: 0, leading: 0, bottom: 0, trailing: 20))
Button(action: {
}, label: {
Image(systemName: "slider.horizontal.3")
.font(.title)
})
}
))
}
}
struct TabView2: View {
var body: some View {
Text("TabView 2")
}
}
What am I missing here?
A NavigationView can be embedded in a TabView and not vice-versa.
TabView contains different tabItem() (at most 5) that can contain your views.
This is how you can use it.
TabView1.swift
struct TabView1: View {
var body: some View {
NavigationView {
Text("TabView 1")
.navigationBarTitle("Dashboard")
.navigationBarItems(trailing:
HStack {
Button(action: {
// more code here
}) {
Image(systemName: "arrow.clockwise")
.font(.title)
}
Button(action: {
// more code here
}) {
Image(systemName: "slider.horizontal.3")
.font(.title)
}
}
)
}
}
}
TabView2.swift
struct TabView2: View {
var body: some View {
NavigationView {
NavigationLink(destination: YourNewView()) {
Text("TabView 1")
}
.navigationBarTitle("Orders")
}
}
}
ContentView.Swift
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
TabView1()
.tabItem {
Image(systemName: "gauge")
Text("Dashboard")
}
TabView2()
.tabItem {
Image(systemName: "list.dash")
Text("Orders")
}
}
}
}
Hope it helps :)

SwiftUI content is not displayed on top of the screen

I have a NavigationView and then a VStack. The issue is that everything inside VStack is being displayed in the middle of the screen and not on top. Why is that?
var body: some View {
VStack {
VStack(alignment: .leading) {
if (request?.receiverID ?? "") == userDataViewModel.userID {
Text("\(sendertFirstName) wants to ship your package").font(.title)
} else {
Text("You want to ship \(recieverFirstName)'s package").font(.title)
}
HStack{
Image(systemName: "clock.fill").font(.subheadline)
Text("\(createdAt, formatter: DateService().shortTime)").font(.subheadline).foregroundColor(.secondary)
}
HStack {
VStack(alignment: .leading) {
HStack {
Image(systemName: "person.fill").font(.subheadline)
Text(sendertFirstName).font(.subheadline).foregroundColor(.secondary)
}
}
Spacer()
VStack(alignment: .leading) {
HStack {
Image(systemName: "star.fill")
Text(rating).font(.subheadline).foregroundColor(.secondary)
}
}
}
}
VStack(alignment: .center) {
HStack {
Button("Accept") {
//print(self.request.createdAt)
//FirestoreService().respondToRequest(request: self.request.documentReference, status: "accepted")
}
.padding()
Button("Decline") {
//FirestoreService().respondToRequest(request: self.request.documentReference, status: "declined")
}
.foregroundColor(.red)
.padding()
}
}
ScrollView {
ForEach(messages) { (message: Message) in
if message.senderId == self.userDataViewModel.userID {
HStack {
Spacer()
Text(message.text).font(.subheadline).padding(7.5).background(self.blue).cornerRadius(30).foregroundColor(.white)
}.padding(.bottom, 2)
} else {
HStack {
Text(message.text).font(.subheadline).padding(7.5).background(self.gray).cornerRadius(30).foregroundColor(.white)
Spacer()
}.padding(.bottom, 2)
}
}
}
HStack {
TextField("Message", text: $inputMessage).padding(5).background(self.gray).cornerRadius(30).foregroundColor(.white)
Button(action: {
let msg: [String: Any] = [
"text": self.inputMessage,
"createdAt": Timestamp(),
"senderId": self.userDataViewModel.userID
]
self.reference.updateData([
"messages": FieldValue.arrayUnion([msg])
])
self.messages.append(Message(dictionary: msg))
self.inputMessage = ""
}) {
Image(systemName: "arrow.up.circle.fill").foregroundColor(self.blue).font(.title)
}
}
Spacer()
}
.padding()
.border(Color.red)
}
I want the content to start on top not on the middle. What am I doing wrong here?
You don't need the NavigationView - the way you have everything set up now you are wrapping the VStack in a NavigationView twice and the massive white space is the second empty navigation bar.
By default most Views take only the amount of space they need and they are placed in the center of their parent view. So if you want your content to be placed at the top, you need to add Spacer() as the last element in your VStack:
var body: some View {
VStack {
/* ... */
Spacer()
}
}
Spacer is one of the rare Views that takes all space offered to it by the parent view and will push all of your content upwards.
Maybe you can try this way:
NavigationView {
VStack {
...
}.frame(maxHeight:.infinity, alignment: .top)
.padding()
.border(Color.red).navigationBarTitle("", displayMode: .inline)
}
Add a .navigationBarHidden(true) to your VStack

Shortening swiftUI code: compiler is unable to type-check this expression in reasonable time

I am trying to create a UI in SwiftUI with two sets of ten buttons (Imaging a game of Cup Pong). Whenever I try to build or preview the code I get the following error message: 'The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions'. I was wondering how I could fix this.
I know that it is very long. Is there any way to fix it so that the code would work.
// ContentView.swift
// Text Pong
//
// Created by Thomas Braun on 8/21/19.
// Copyright © 2019 Thomas Braun. All rights reserved.
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(spacing: 250.0) {//Contains both the triangles
VStack {//User Triangle
HStack(spacing: 15.0) {
Button(action: {}) {
Text("7")
.font(.largeTitle)
}
Button(action: {}) {
Text("8")
.font(.largeTitle)
}
Button(action: {}) {
Text("9")
.font(.largeTitle)
}
Button(action: {}) {
Text("10")
.font(.largeTitle)
}
}
HStack(spacing: 15.0) {
Button(action: {}) {
Text("6")
.font(.largeTitle)
}
Button(action: {}) {
Text("5")
.font(.largeTitle)
}
Button(action: {}) {
Text("4")
.font(.largeTitle)
}
}
HStack(spacing: 15.0) {
Button(action: {}) {
Text("3")
.font(.largeTitle)
}
Button(action: {}) {
Text("2")
.font(.largeTitle)
}
}
HStack(spacing: 15.0) {
Button(action: {}) {
Text("1")
.font(.largeTitle)
}
}
}
// Text("Game On")
VStack {//Opponent Triangle
HStack {
VStack {
Button(action: {}) {
Text("1")
.font(.largeTitle)
}
HStack {
Button(action: {}) {
Text("2")
.font(.largeTitle)
}
Button(action: {}) {
Text("3")
.font(.largeTitle)
}
}
HStack {
Button(action: {}) {
Text("4")
.font(.largeTitle)
}
Button(action: {}) {
Text("5")
.font(.largeTitle)
}
Button(action: {}) {
Text("6")
.font(.largeTitle)
}
}
HStack {
Button(action: {}) {
Text("7")
.font(.largeTitle)
}
Button(action: {}) {
Text("8")
.font(.largeTitle)
}
Button(action: {}) {
Text("9")
.font(.largeTitle)
}
Button(action: {}) {
Text("10")
.font(.largeTitle)
}
}
}
}
}// Ending Opponent Triangle verticle Stack
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Break it into smaller parts. For example by each row and then by each player like this:
struct OpponentTriangleView: View {
var body: some View {
VStack {//Opponent Triangle
HStack {
VStack {
Part1View()
Part2View()
Part3View()
Part4View()
}
}
}// Ending Opponent Triangle vertical Stack
}
}
And define each part like this:
extension OpponentTriangleView {
struct Part1View: View {
var body: some View {
HStack {
Button(action: {}) { Text("1") .font(.largeTitle) }
}
}
}
struct Part2View: View {
var body: some View {
HStack {
Button(action: {}) { Text("2").font(.largeTitle) }
Button(action: {}) { Text("3").font(.largeTitle) }
}
}
}
struct Part3View: View {
var body: some View {
HStack {
Button(action: {}) { Text("4").font(.largeTitle) }
Button(action: {}) { Text("5").font(.largeTitle) }
Button(action: {}) { Text("6").font(.largeTitle) }
}
}
}
struct Part4View: View {
var body: some View {
HStack {
Button(action: {}) { Text("7").font(.largeTitle) }
Button(action: {}) { Text("8").font(.largeTitle) }
Button(action: {}) { Text("9").font(.largeTitle) }
Button(action: {}) { Text("10").font(.largeTitle) }
}
}
}
}
And similarly define UsertTriangleView. Then use them like this:
struct ContentView: View {
var body: some View {
VStack(spacing: 250.0) {//Contains both the triangles
UserTriangleView()
// Text("Game On")
OpponentTriangleView()
}
}
}
And you are good to go
- Notes:
Not only in SwiftUI, but always break huge codes into the smaller meaningful pieces.
Don`t Repeat Yourself. Try to create some builder function or use loops to achieve repeating tasks without actually writing it again and again.
I've seen Xcode (12.5.1) show this when I had a build error in my view. Try commenting out portions of your view and rebuilding, it will eventually show you what the real error is. In my case I was missing a constructor argument for one of my child views. After you find the real problem and fix it, you can uncomment everything and it should build fine now.
In my case a lot of the #State was the reason causing this error. After removing one it started to work