Error creating a custom PageView in SwiftUI 2 - swiftui

I am working on an app that requires 6 items in a PageView, xcode was acting up so i came on Stack Overflow and found pawello2222's way, found it really helpful but I still need to connect it to the horizontal links on top. here is the code, thanks a million.
Original solution: How can I implement PageView in SwiftUI?
Below is my code and what I want to achieve, swiping left or right works, but clicking on the links does not change the view
struct TestingView: View {
#State var selection = 0
var body: some View {
VStack {
HStack(spacing: 10) {
VStack {
Text("Link 1")
.foregroundColor(self.selection == 0 ? Color.blue : Color("Silver").opacity(0.7))
.font(.caption)
.fontWeight(.bold)
.clipShape(Rectangle())
.onTapGesture {
withAnimation(.default) {
self.selection = 0
}
}
}
Spacer(minLength: 0)
VStack {
Text("Link 2")
.foregroundColor(self.selection == 1 ? Color.blue : Color("Silver").opacity(0.7))
.font(.caption)
.fontWeight(.bold)
.clipShape(Rectangle())
.onTapGesture {
withAnimation(.default) {
self.selection = 1
}
}
}
Spacer(minLength: 0)
VStack {
Text("Link 3")
.foregroundColor(self.selection == 2 ? Color.blue : Color("Silver").opacity(0.7))
.font(.caption)
.fontWeight(.bold)
.clipShape(Rectangle())
.onTapGesture {
withAnimation(.default) {
self.selection = 2
}
}
}
}
PageView(selection: $selection, indexDisplayMode: .never, indexBackgroundDisplayMode: .never) {
VStack {
FirstView()
}
.tag(0)
VStack {
SecondView()
}
.tag(1)
VStack {
ThirdView()
}
.tag(2)
}
}
}
}

Related

Animate text position simultaneously with text changing in SwiftUI

I want to animate the position of Expand button, but instead it appears right at the final position. There's a strange hack .transition(.scale) that fixes my problem, but I hope to see a better not hacky solution.
struct TextView: View {
#State var isExpanded = false
var body: some View {
VStack(spacing: 10) {
Text("This is the most recent test comment with a picture. Lörem ipsum berölogi kjolprotest. Trist gigade. Sms-livräddare grönt elcertifikat.")
.lineLimit(isExpanded ? nil : 2)
HStack {
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Spacer()
Text(isExpanded ? "Less" : "Expand")
}
// .transition(.scale)
}
}
.padding(.all)
.background(Color.yellow)
.cornerRadius(13)
.padding(.horizontal)
}
}
One option is to use a ZStack containing Buttons in both states, and use the .opacity modifier to hide or show them…
struct ContentView: View {
#State var isExpanded = false
var body: some View {
VStack(spacing: 10) {
Text("This is the most recent test comment with a picture. Lörem ipsum berölogi kjolprotest. Trist gigade. Sms-livräddare grönt elcertifikat.")
.lineLimit(isExpanded ? nil : 2)
HStack {
ZStack {
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Spacer()
Text("Less")
}
.opacity(isExpanded ? 1 : 0)
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Spacer()
Text("Expand")
}
.opacity(isExpanded ? 0 : 1)
}
}
}
.padding(.all)
.background(Color.yellow)
.cornerRadius(13)
.padding(.horizontal)
}
}
Or alternatively, use .matchedGeometryEffect
if isExpanded {
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Spacer()
Text("Less")
}
.matchedGeometryEffect(id: "button", in: namespace)
} else {
Button {
withAnimation {
isExpanded.toggle()
}
} label: {
Spacer()
Text("Expand")
}
.matchedGeometryEffect(id: "button", in: namespace)
}
Obviously there's some duplication so you would pull the Button out into a func or its own View struct.

Color behind the time on iOS

This is my code:
struct Account: View {
var body: some View {
VStack {
ScrollView {
HStack {
Text("Account")
.font(.largeTitle)
.fontWeight(.bold)
Spacer(minLength: 0)
}
.padding()
.background(Color.indigo)
VStack {
Text("Doe, John Jack")
.font(.title)
Divider()
.foregroundColor(Color.indigo)
HStack {
Text("")
}
}
Spacer(minLength: 0)
VStack {
Button(action: {
}) {
Text("Log Out")
.foregroundColor(.red)
.fontWeight(.bold)
}
}
}
}
}
}
If you run the code above, you will see that the indigo doesn't go behind the time and battery precentage. How can I make it do that?
Try like this:
struct Account: View {
var body: some View {
VStack {
ScrollView {
HStack {
Text("Account")
.font(.largeTitle)
.fontWeight(.bold)
Spacer(minLength: 0)
}
.padding()
.background(Color.indigo)
VStack {
Text("Doe, John Jack")
.font(.title)
Divider()
.foregroundColor(Color.indigo)
HStack {
Text("")
}
}
Spacer(minLength: 0)
VStack {
Button(action: {
}) {
Text("Log Out")
.foregroundColor(.red)
.fontWeight(.bold)
}
}
}.padding(.top, 66)
}
.background(Color.indigo)
.edgesIgnoringSafeArea(.all)
}
}
You would also have to add some top padding to the ScrollView since ignoring safe area is going to push all your views to the margins
You should ignore the top safe area for this:
.ignoresSafeArea(.container, edges: .top)
Full working example
struct ContentView: View {
var body: some View {
Color
.indigo
.ignoresSafeArea(.container, edges: .top)
}
}

SwiftUI: How to update image to filled / outlined in TabView

I'm trying to use filled image when it is selected and outlined image when it is deselected. I tried to render the images but still filled
So I thought this would work, but it doesn't:
struct ListTabView: View {
#State private var selectedTab = 0
var body: some View {
NavigationView {
TabView(selection: $selectedTab) {
Text("Tab 1")
.onTapGesture {
self.selectedTab += 1
}
.tabItem {
selectedTab == 0 ? Image(systemName: "star.fill") : Image(systemName: "star")
Text("First")
}
.tag(0)
Text("Tab 2")
.onTapGesture {
self.selectedTab -= 1
}
.tabItem {
selectedTab == 1 ? Image(systemName: "moon.stars.fill") : Image(systemName: "moon.stars")
Text("Second")
}
.tag(1)
}
.accentColor(.pink)
.onAppear {
UITabBar.appearance().barTintColor = .white
}
}
}
}
struct ListTabView_Previews: PreviewProvider {
static var previews: some View {
ListTabView()
}
}
Your code actually works. The issue is something else not documented that I can find. If you use a non .fill variant of an SF Font, the .fill variant will be substituted. Use the following code to test it:
TabView(selection: $selectedTab) {
VStack {
Text("Tab 1")
Text(Image(systemName: "star"))
}
.tabItem {
selectedTab == 0 ? Image(systemName: "star") : Image(systemName: "sun.max")
Text("First")
}
.tag(0)
VStack {
Text("Tab 2")
Text(Image(systemName: "moon.stars"))
}
.tabItem {
selectedTab == 1 ? Image(systemName: "moon.stars") : Image(systemName: "sun.max")
Text("Second")
}
.tag(1)
}
You will note I used plain variants, and yet the filled variant was used. Also, you don't need the .onTap(), but I suspect you added it when the images didn't seem to switch.
You can add this to Image / Label:
Image(systemName: selectedTab == 0 ? "star.fill" : "star")
.environment(\.symbolVariants, selectedTabItemIndex == 0 ? .fill : .none)
It will allow you to set the symbol variants as you would expect it.

Navigation bar with UIImage for logo in SwiftUI [duplicate]

This question already has answers here:
How to put a logo in NavigationView in SwiftUI?
(8 answers)
Having Tab bar and Navigationbar in the same View in SwiftUI?
(2 answers)
Closed 1 year ago.
I want to use a logo image in the navigation bar's title, instead of plain text. Here some solution for UIKit, but I want to use for SwiftUI also. Any idea will be appreciated.
Screenshot:
ContentView:
struct ContentView: View {
#State private var isShowing = false
var body: some View {
ZStack {
if isShowing {
SideMenuView(isShowing: $isShowing)
}
TabView {
NavigationView {
HomeView()
.navigationBarItems(leading: Button(action: {
withAnimation(.spring()) {
isShowing.toggle()
}
} , label: {
Image(systemName: "list.bullet")
}))
}
.tabItem {
Image(systemName: "1.circle")
Text("Page 1")
}
NavigationView {
HomeTwoView()
.navigationBarItems(leading: Button(action: {
withAnimation(.spring()) {
isShowing.toggle()
}
} , label: {
Image(systemName: "list.bullet")
}))
}
.tabItem {
Image(systemName: "2.circle")
Text("Page 2")
}
}
.edgesIgnoringSafeArea(.bottom)
//.cornerRadius(isShowing ? 20 : 0) //<< disabled due to strange effect
.offset(x: isShowing ? 300 : 0, y: isShowing ? 44: 0)
.scaleEffect(isShowing ? 0.8 : 1)
}.onAppear {
isShowing=false
}
}
}
Solution is here. You can add .toolbar {ToolbarItem(placement: .principal) {Image(systemName: "star.fill")} } inside of the NavigationView.
NavigationView {
HomeView()
.toolbar {
ToolbarItem(placement: .principal) {
Image(systemName: "star.fill")
}
}
.navigationBarItems(leading: Button(action: {
withAnimation(.spring()) {
isShowing.toggle()
}
} , label: {
Image(systemName: "list.bullet")
}))
}
Screenshot:

TabView tabItem image move to top

I learned how to create a tabBar like UIKit tabBar in swiftUI. And I want to move the center tabItem to top . Is there any way I can achieve this?
TabView code
TabView {
ViewTasks()
.tabItem {
Image(systemName: "list.bullet.below.rectangle")
Text("View Tasks")
}
AddTask()
.tabItem {
Image(systemName: "plus.circle").font(.system(size: 60))
}
CategoriesTasks()
.tabItem {
Image(systemName: "square.grid.2x2")
Text("Categories")
}
}
Visual Example
First idea is to use ZStack so you can cover tabItem with your own tappable image. Here is example:
struct TabViewModel: View {
#State var showActionSheet: Bool = false
var body: some View {
ZStack {
GeometryReader { geometry in
TabView {
Text("list")
.tabItem {
Image(systemName: "list.bullet.below.rectangle")
}
Text("plus")
.tabItem {
Text(" ")
}
Text("categories")
.tabItem {
Image(systemName: "square.grid.2x2")
}
}
Image(systemName: "plus.circle")
.resizable()
.frame(width: 40, height: 40)
.shadow(color: .gray, radius: 2, x: 0, y: 5)
.offset(x: geometry.size.width / 2 - 20, y: geometry.size.height - 80)
.onTapGesture {
self.showActionSheet.toggle()
}
}
}
.actionSheet(isPresented: $showActionSheet) {
ActionSheet(title: Text("some actions"))
}
}
}
so some image will cover center tabView item, which will be invisible (Text(" ")):
update
if you still need to switch between these 3 views you can use #State var selection (don't forget to tag(...) tabItems):
struct TabViewModel: View {
#State var selection: Int = 0
var body: some View {
ZStack {
GeometryReader { geometry in
TabView(selection: self.$selection) {
//...
Text("plus")
.tabItem {
Text(" ")
}.tag(1)
//...
.onTapGesture {
self.selection = 1
}
// ...
}
SWIFTUI 2
.onTapGesture usage sometimes causes unexpected behavior such as not displaying the overlay view (alert, sheets or fullscreen) or displaying it when you click another tab. The safer way is to set the selection value with the .onChange(of:) method:
struct TabViewModel: View {
#State var selection: Int = 0
var body: some View {
ZStack {
GeometryReader { geometry in
TabView(selection: self.$selection) {
//...
Text("plus")
.tabItem {
Text(" ")
}.tag(1)
//...
.onChange(of: selection) { _ in
self.selection = 1
}
// ...
}