Swiftui Scrollview text change color onTapGesture - uiscrollview

I want change color of the Text item onTapGesture for simulate the selected item, when click the Text color should be Red and other Text Black.
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
ForEach(newsFeed.subcategoryListNews) { item in
Text(item.name)
.font(Font.custom("AvenirNextLTPro-Demi", size: 17))
.foregroundColor(Color.black)
.onTapGesture {
//Chage color and reset other Text, active red not active black
}
}
}
.padding(.top, 30)
.padding(.horizontal, 30)
}.padding(.bottom, 10)
Many thanks

It needs to conform your item type Equatable (if it is not yet) then the following approach is possible
#State private var selectedItem: <Item_Type_Here>? = nil
...
ForEach(newsFeed.subcategoryListNews) { item in
Text(item.name)
.font(Font.custom("AvenirNextLTPro-Demi", size: 17))
.foregroundColor(self.selectedItem == item ? Color.red : Color.black))
.onTapGesture {
self.selectedItem = item
}
}

Related

SwiftUI transition with opacity not showing

I'm still new to SwiftUI. I'm trying to get each change of an image to start out at opacity 0.0 (fully transparent), then increase to opacity 1.0 (fully opaque)
I expected I could achieve this using the .opacity transition. .opacity is described as a "transition from transparent to opaque on insertion", so my assumption is that by stating "withAnimation" in my Button action, I'd trigger the Image to be re-rendered, and the transition would occur beginning from faded to transparent. Instead I see the same instant appear of the new shape & slow morphing to a new size, no apparent change in .opacity. Code and .gif showing current result, below. I've used UIKit & know I'd set alpha to zero, then UIView.animate to alpha 1.0 over a duration of 1.0, but am unsure how to get the same effect in SwiftUI. Thanks!
struct ContentView: View {
#State var imageName = ""
var imageNames = ["applelogo", "peacesign", "heart", "lightbulb"]
#State var currentImage = -1
var body: some View {
VStack {
Spacer()
Image(systemName: imageName)
.resizable()
.scaledToFit()
.padding()
.transition(.opacity)
Spacer()
Button("Press Me") {
currentImage = (currentImage == imageNames.count - 1 ? 0 : currentImage + 1)
withAnimation(.linear(duration: 1.0)) {
imageName = imageNames[currentImage]
}
}
}
}
}
The reason you are not getting the opacity transition is that you are keeping the same view. Even though it is drawing a different image each time, SwiftUI sees Image as the same. the fix is simple: add .id(). For example:
Image(systemName: imageName)
.resizable()
.scaledToFit()
.padding()
.transition(.opacity)
// Add the id here
.id(imageName)
Here is the correct approach for this kind of issue:
We should not forgot how transition works!!! Transition modifier simply transmit a view to nothing or nothing to a view (This should be written with golden ink)! in your code there is no transition happening instead update happing.
struct ContentView: View {
#State var imageName1: String? = nil
#State var imageName2: String? = nil
var imageNames: [String] = ["applelogo", "peacesign", "heart", "lightbulb"]
#State var currentImage = -1
var body: some View {
VStack {
Spacer()
ZStack {
if let unwrappedImageName: String = imageName1 {
Image(systemName: unwrappedImageName)
.resizable()
.transition(AnyTransition.opacity)
.animation(nil, value: unwrappedImageName)
.scaledToFit()
}
if let unwrappedImageName: String = imageName2 {
Image(systemName: unwrappedImageName)
.resizable()
.transition(AnyTransition.opacity)
.animation(nil, value: unwrappedImageName)
.scaledToFit()
}
}
.padding()
.animation(.linear, value: [imageName1, imageName2])
Spacer()
Button("Press Me") {
currentImage = (currentImage == imageNames.count - 1 ? 0 : currentImage + 1)
if imageName1 == nil {
imageName2 = nil; imageName1 = imageNames[currentImage]
}
else {
imageName1 = nil; imageName2 = imageNames[currentImage]
}
}
}
}
}

How to add animation to sheet dismissal

Presenting a modal sheet in SwiftUI MacOS has a nice slide in animation, but when dismissed it just disappear.
I would like to add a slide out animation to the dismissal of the sheet.
Adding the slideout on the content of the sheet obviously doesn’t work, as the content slides out, but the sheet frame remains until dismissed.
I might be missing something obvious here, but how can I add animation to the sheet itself?
struct ModalSheet: View {
#Binding var visible: Bool
#State var isShowing: Bool = true
var body: some View {
let s = "Click me to dismiss " + String(isShowing);
VStack {
Text(s)
}
.frame(width: 200, height: 100, alignment: .center)
.background(Color.green.opacity(0.5))
.onTapGesture {
isShowing.toggle()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
visible.toggle()
}
}
.offset(y: self.isShowing ? 0 : CGFloat(-110)) // <-- this should be on the sheet itself
.animation(self.isShowing ? .none : .default)
}
}
struct ModalTestAnimation: View {
#State var visible: Bool = false;
var body: some View {
VStack {
Text("Click me")
.onTapGesture {
visible = true
}
}
.frame(width: 250, height: 150)
.sheet(isPresented: $visible, content: {
ModalSheet(visible: $visible)
})
}
}
I did take a look at this, but it doesn’t work for me
SwiftUI sheet not animating dismissal on macOS Big Sur

Animation with offset lags behind

I want a navigation bar to stick down to the scrollview when scrolling up beyond the "regular scrollview". I use .offset() and GeometryReader for that and it's working. However, the navigation bar noticeably lags behind: Video.
Is there another approach to achieving the sticky navigation bar or something that can be changed about this one? Am I using too many views?
struct V_Home: View {
var previewData = PreviewData()
#State var size: CGRect = .zero
var body: some View {
GeometryReader { geometry in
ZStack {
ScrollView {
VStack {
// used to read the scroll position
GeometryReader { proxy in
Color.clear
.preference(key: SizePreferenceKey.self, value: proxy.frame(in: .named("scrollView")))
}
.frame(height: 0)
.onPreferenceChange(SizePreferenceKey.self) { preferences in
self.size = preferences
}
// List
ForEach(previewData.ScoreSessionList) { scoreSession in
NavigationLink(destination: V_SessionDetail(scoreSession: scoreSession)) {
HStack(spacing: 0) {
V_ScoreSessionListItem(scoreSession: scoreSession)
}
}.padding(.top, 10)
}
.padding([.leading, .trailing], 25)
}
}
.coordinateSpace(name: "scrollView")
// NavBar
VStack {
// This Rectangle is offset to match the scroll position
// Is is lagging behind noticably
Rectangle()
.fill(Color(.green))
.frame(height: 80)
.offset(y: self.size.minY > 0 ? self.size.minY : 0)
.padding(0)
Spacer()
}
}
.edgesIgnoringSafeArea(.all)
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
// used to make scrollview position accessible to other view
struct SizePreferenceKey: PreferenceKey {
typealias Value = CGRect
static var defaultValue: Value = .zero
static func reduce(value _: inout Value, nextValue: () -> Value) {
_ = nextValue()
}
}

Navigation bar Display Mode is not working Perfectly in SwiftUI

I'm using SwiftUI with Version 11.3.1 (11C504).
I Implement Tab bar on the Screen and that screen will come after the navigation from login Screen.
If navigation displayMode is .automatic so i’am showing a white space(which is in bordered with black line in picture) when I scroll a list in up side. Otherwise navigation title show.
And When I set displayMode, .inline so it works perfect. And when I run a project so many time so some time it works perfect and some time showing a space.
i mentioned a white space in the picture with black border.
//ContentView
struct ContentView: View {
var body: some View {
HomeTabView()
}
}
//HomeTabView
struct HomeTabView: View {
#State private var selection = 0
//Inclose user intraface of tab View.
var body: some View {
TabView(selection: $selection){
TestListView().tabItem {
Image(systemName: "book.fill")
Text("Learn")
}.tag(0)
Text("Community").tabItem {
Image(systemName: "globe")
Text("Community")
}.tag(1)
//Add Notification List on the Screen.
Text("Notification").tabItem {
Image(systemName: "bell.fill")
Text("Notification")
}.tag(3)
//Add Account on the Tab Bar
Text("Account").tabItem {
Image(systemName: "person.circle.fill")
Text("Account")
}.tag(4)
}.accentColor(.pink)
.navigationBarTitle("SwiftUI")
}
}
TestListView
struct TestListView: View {
var body: some View {
VStack{
List(1...10, id: \.self){ num in
ListCards()
}
}.edgesIgnoringSafeArea(.all)
}
}
//ListCards
struct ListCards: View {
var body: some View {
ZStack{
RoundedRectangle(cornerRadius: 16)
.frame(height: 180)
.foregroundColor(.white)
.shadow(radius: 5)
VStack(alignment: .leading, spacing: 10){
HStack(alignment: .top){
Rectangle()
.frame(width: 100, height: 100)
.cornerRadius(16)
.foregroundColor(.pink)
VStack(alignment: .leading, spacing: 4){
Text("SwiftUI")
.font(.title)
Text("Description of title")
.foregroundColor(.gray)
}
}
.padding()
}.padding(.leading, 2)
}.padding(.all, 6)
}
}
You need to change your HomeTabView in this way:
var body: some View {
TabView(selection: $selection) {
NavigationView {
TestListView()
.navigationBarTitle("SWIFTUI")
}.tabItem {
Image(systemName: "book.fill")
Text("Learn")
}.tag(0)
NavigationView {
Text("Community")
}.tabItem {
Image(systemName: "globe")
Text("Community")
}.tag(1)
//Add Notification List on the Screen.
NavigationView {
Text("Notification")
}.tabItem {
Image(systemName: "bell.fill")
Text("Notification")
}.tag(3)
//Add Account on the Tab Bar
NavigationView {
Text("Account")
}.tabItem {
Image(systemName: "person.circle.fill")
Text("Account")
}.tag(4)
}.accentColor(.pink)
}
also, remove .edgesIgnoringSafeArea(.all) from TestListView

SwifUI: How do I align padding value of an element based on position of another element

How do I align elements in two different HStacks contained within a VStack in SwiftUI?
Here's what I'm trying to do -
VStack {
HStack {
element A - Text
element B - Image
element C - Image
}
HStack {
element D - Text
element E - Text
}
}
How do I align 'Text element D' in the 2nd HStack based on the center axis of 'Text element A' in the 1st HStack?
If I use the .center alignment on the all enclosing VStack it aligns the HStacks but not the individual elements to each other
Thanks in advance!
EDIT 1:
#E.coms
Based on your input this is what I'm doing
enter code here
import SwiftUI
struct ContentView2: View {
var body: some View {
VStack(alignment: .center) {
Image(systemName: "photo")
.padding(.bottom, 10)
VStack(alignment: .midNameAndDate) {
HStack{
Text("Name")
.padding(.trailing, 100)
.alignmentGuide(.midNameAndDate, computeValue: { v in v[HorizontalAlignment.center] })
Image(systemName: "phone")
.padding(.trailing, 20)
Image(systemName: "location")
.padding(.trailing, 40)
}
HStack {
Text("Month day, year")
.padding(.trailing, 40)
Text("00:00 ZM")
.padding(.trailing, 40)
}.alignmentGuide(.midNameAndDate) { v in v[.leading]}
}
}
}
}
struct ContentView2_Previews: PreviewProvider {
static var previews: some View {
ContentView2()
}
}
extension HorizontalAlignment {
private enum MidNameAndDate : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
return d[.leading]
}
}
static let midNameAndDate = HorizontalAlignment(MidNameAndDate.self)
}
'''
This is what I end up with
This is what I need instead
Should I rather split each of them as a VStack within a HStack?
On the other hand I am able to align them by splitting them as individual vertical stack (i.e. Name & Date under one vertical stack and Phone, Location & Time under another vertical stack. With both vertical stacks under a horizontal stack). But this becomes increasingly complicated when I want to create a list of these elements.
I was able to find information on aligning UIelements in different horizontal stacks at the WWDC 2019 videos, but nothing on aligning UIelements in different vertical stacks, at least I hope I'm not overlooking something!
I show you a sample like the following:
extension HorizontalAlignment {
private enum MyAlignment: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
return d[.trailing]
}
}
static let myAlignment = HorizontalAlignment(MyAlignment.self)
}
VStack(alignment: .myAlignment) {
HStack {
(element A).alignmentGuide(.myAlignment) { v in v[HorizontalAlignment.center]}
(element B)
(element C)
}
HStack {
(element D)
(element E)
}.alignmentGuide(.myAlignment) { v in v[.leading]}
}
In you latest example, it should be like the following:
struct ContentView2: View {
var body: some View {
VStack(alignment: .midNameAndDate) {
Image(systemName: "photo")
.padding(.bottom, 10).alignmentGuide(.midNameAndDate) { v in v[HorizontalAlignment.center]}
VStack(alignment: .midNameAndDate) {
HStack{
Text("Name")
.padding(.trailing, 100)
.alignmentGuide(.midNameAndDate, computeValue: { v in v[HorizontalAlignment.center] + 25 })
Image(systemName: "phone")
.padding(.trailing, 20)
Image(systemName: "location")
.padding(.trailing, 40)
}
HStack {
Text("Month day, year").alignmentGuide(.midNameAndDate) { v in v[.trailing]}
.padding(.trailing, 40)
Text("00:00 ZM")
.padding(.trailing, 40)
}
}
}.frame(maxWidth: .infinity, alignment: .trailing)
}
}
You can image the customized horizontal alignment is a line on your view elements. They should be at the same position with same midNameAndDate. Because you are using arbitrary trailing padding, you have to use the constants in your adjustment.
As to the container, use frame alignment to position the whole view in any superView.

Resources