SwiftUI not very responsive to hover events - macos

I'm trying to implement a list where I have 200 or 300 elements, and I want to change the color of the text on a hover event. But the app starts to show a delay on the hover events. Check the example code below:
struct ContentView: View {
var body: some View {
VStack {
ForEach(0...1000, id:\.self) {index in
Element()
}
}
}
}
struct Element: View {
#State private var hover = false
var body: some View {
Text("Not a fast hover!")
.foregroundColor(hover ? Color.blue : Color.white)
.onHover {_ in self.hover.toggle()}
}
}
UPDATE:
This seems to improve the responsiveness. Also if I change the background instead of the foreground color, the code is also more responsive.
struct Element: View {
#State private var hover = false
var body: some View {
ZStack {
Text("Not a fast hover!").foregroundColor(Color.blue)
Text("Not a fast hover!").opacity(hover ? 0 : 1).foregroundColor(Color.white)
}
.frame(width: 200)
.onHover {_ in self.hover.toggle()}
}
}

The solution was to use a List component instead of a VStack.

Related

Displaying View via .sheet() causes AttributeGraph: cycle detected through attribute

If i display the same custom view in ContentView everthing is fine, but displaying in a modal .sheet() view causes attributeGraph cycle errors. If i comment out the call in the ContentView, the problem remains. This does not affect apps behaviour, so far. I suggest that the LazyVGrid makes in modal situations via .sheet() trouble. Is there any work around?
xcode version 13.4.1 and macOS (12.4) target
struct ContentView: View {
#State private var dialogSimple = false
var buttons: some View {
HStack {
Button("simple") {
dialogSimple = true
}
.sheet(isPresented: $dialogSimple) {
DialogSimple() // with Graph cycle error !
}
}
.padding(.all)
}
var body: some View {
VStack {
DataView() // without Graph cycle error !
buttons
Text("Test Dialog via .sheet() shows error:")
Text("AttributeGraph: cycle detected through attribute ...")
}
.padding(.all)
.font(.headline)
}
}
struct DialogSimple: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
VStack {
DataView()
Button("Close") {
self.presentationMode.wrappedValue.dismiss()
}
.padding(.all)
}
}
}
struct DataView: View {
var columns: [GridItem] = Array(repeating: .init(.flexible()), count: 2)
var body: some View {
LazyVGrid(columns: columns) {
Text("Field 1")
Text("Value1")
Text("Field 2")
Text("Value2")
}.frame(width: 300.0).font(.body)
}
}

SwiftUI: how to make a view disappear with a slide to the top?

I would like to make a view disappear with a transition to the top, but I have no idea how to do this with SwiftUI. Here is my code so far:
struct MyList: View {
#State private var loading = true
#State private var elements: [MyElement] = []
var body: some View {
ZStack {
Color.white
List(elements) {
ListRow($0)
}
if loading {
LoadingView() // This is the view I want to slide to the top when hiding
.ignoresSafeArea()
}
}
.onAppear {
callAPI() { elements in
self.elements = elements
self.loading = false
}
}
}
}
I want to hide the LoadingView() with a slide-to-top transition, but don't know how.
Thank you for your help!
You could use the .transition modifier.
documentation
LoadingView()
.transition(.move(edge: .top))
But donĀ“t forget to animate it:
.onAppear {
self.loading = true
callAPI() { elements in
self.elements = elements
DispatchQueue.main.async { // as this is probably from background Thread dispatch it
self.loading = false
}
}
}.animation(.default, value: loading)

Highlight Navigation View At Start

When I start my app, the start page is "Kunde" but the whole thing is not highlighted in blue in the navigation. It just turns blue (system color) when I click on it.
I want it to be highlighted blue when I open the app.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination: ListView()) {
Text("Kunde")
}
}
ListView()
}
}
}
struct ListView: View {
var body: some View {
Text("Hello.")
}
}
you could try something like this approach:
struct ContentView: View {
#State var selection: String?
#State var listData = ["Kunde", "xxxx", "zzzz"]
var body: some View {
NavigationView {
List(listData, id: \.self) { item in
NavigationLink(destination: ListView()) {
Text(item)
}
.listRowBackground(selection == item ? Color.blue : Color.clear)
}
}
.onAppear {
selection = "Kunde"
}
}
}

swiftui, animation applied to parent effect child animation

RectangleView has a slide animation, his child TextView has a rotation animation. I suppose that RectangleView with his child(TextView) as a whole slide(easeInOut) into screen when Go! pressed, and TextView rotate(linear) forever. But in fact, the child TextView separates from his parent, rotating(linear) and sliding(linear), and repeats forever.
why animation applied to parent effect child animation?
struct AnimationTestView: View {
#State private var go = false
var body: some View {
VStack {
Button("Go!") {
go.toggle()
}
if go {
RectangleView()
.transition(.slide)
.animation(.easeInOut)
}
}.navigationTitle("Animation Test")
}
}
struct RectangleView: View {
var body: some View {
Rectangle()
.frame(width: 100, height: 100)
.foregroundColor(.pink)
.overlay(TextView())
}
}
struct TextView: View {
#State private var animationRotating: Bool = false
let animation = Animation.linear(duration: 3.0).repeatForever(autoreverses: false)
var body: some View {
Text("Test")
.foregroundColor(.blue)
.rotationEffect(.degrees(animationRotating ? 360 : 0))
.animation(animation)
.onAppear { animationRotating = true }
.onDisappear { animationRotating = false }
}
}
If there are several simultaneous animations the generic solution (in majority of cases) is to use explicit state value for each.
So here is a corrected code (tested with Xcode 12.1 / iOS 14.1, use Simulator or Device, Preview renders some transitions incorrectly)
struct AnimationTestView: View {
#State private var go = false
var body: some View {
VStack {
Button("Go!") {
go.toggle()
}
VStack { // container needed for correct transition !!
if go {
RectangleView()
.transition(.slide)
}
}.animation(.easeInOut, value: go) // << here !!
}.navigationTitle("Animation Test")
}
}
struct RectangleView: View {
var body: some View {
Rectangle()
.frame(width: 100, height: 100)
.foregroundColor(.pink)
.overlay(TextView())
}
}
struct TextView: View {
#State private var animationRotating: Bool = false
let animation = Animation.linear(duration: 3.0).repeatForever(autoreverses: false)
var body: some View {
Text("Test")
.foregroundColor(.blue)
.rotationEffect(.degrees(animationRotating ? 360 : 0))
.animation(animation, value: animationRotating) // << here !!
.onAppear { animationRotating = true }
.onDisappear { animationRotating = false }
}
}

Change color of image (icon) in tabItems in SwiftUI

How can i change the color of the image (icon) in the tabItems in SwiftUI?
I tried so many things and nothing works...
Thanks
Here is my test code :
import SwiftUI
struct TestTabviewIconColor: View {
var body: some View {
TabView {
Text("The First Tab")
.tabItem {
Image(systemName: "1.square.fill")
.foregroundColor(.red)
.accentColor(.red)
.colorMultiply(.red)
Text("First")
}
Text("Another Tab")
.tabItem {
Image(systemName: "2.square.fill")
Text("Second")
}
Text("The Last Tab")
.tabItem {
Image(systemName: "3.square.fill")
Text("Third")
}
}
.font(.headline)
}
}
struct TestTabviewIconColor_Previews: PreviewProvider {
static var previews: some View {
TestTabviewIconColor()
}
}
If you want to have different TabBar button colors when the tab is selected than I'm reasonably confident that the Apple provided control won't do that for you.
You'll need to roll your own control. This is fairly straightforward. For an example with a THREE tabs see the code below. If you're working with a fixed number of tabs this approach might work for you. And the principles could be applied to build a control for more and variable number of tabs using #ViewBuilder etc.
I've approximated the styling of the stock TAB bar. You'd need to do a bit of refinement to look exactly the same if that's important for your use case.
struct TabLabel : View {
var text : String
var imageName : String
var color : Color
var body : some View {
VStack() {
Image(systemName: imageName)
Text(text).font(.caption)
}.foregroundColor(color)
}
}
struct TabButton : View {
#Binding var currentSelection : Int
var selectionIndex : Int
var label : TabLabel
var body : some View {
Button(action: { self.currentSelection = self.selectionIndex }) { label }.opacity(selectionIndex == currentSelection ? 0.5 : 1.0)
}
}
struct CustomTabBarView<SomeView1 : View, SomeView2 : View, SomeView3 : View> : View {
var view1 : SomeView1
var view2 : SomeView2
var view3 : SomeView3
#State var currentSelection : Int = 1
var body : some View {
let label1 = TabLabel(text: "First", imageName: "1.square.fill", color: Color.red)
let label2 = TabLabel(text: "Second", imageName: "2.square.fill", color: Color.purple)
let label3 = TabLabel(text: "Third", imageName: "3.square.fill", color: Color.blue)
let button1 = TabButton(currentSelection: $currentSelection, selectionIndex: 1, label: label1)
let button2 = TabButton(currentSelection: $currentSelection, selectionIndex: 2, label: label2)
let button3 = TabButton(currentSelection: $currentSelection, selectionIndex: 3, label: label3)
return VStack() {
Spacer()
if currentSelection == 1 {
view1
}
else if currentSelection == 2 {
view2
}
else if currentSelection == 3 {
view3
}
Spacer()
HStack() {
button1
Spacer()
button2
Spacer()
button3
}.padding(.horizontal, 48)
.frame(height: 48.0)
.background(Color(UIColor.systemGroupedBackground))
}
}
}
struct ContentView: View {
var body: some View {
let view1 = VStack() {
Text("The First Tab").font(.headline)
Image(systemName: "triangle").resizable().aspectRatio(contentMode: .fit).frame(width: 100)
}
let view2 = Text("Another Tab").font(.headline)
let view3 = Text("The Final Tab").font(.headline)
return CustomTabBarView(view1: view1, view2: view2, view3: view3)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
For change tint color of tabbar, you just need set .accentColor() modifier for the TabView to apply on items.
Try changing rendering mode of the image:
In Assets.xcassets select the image, open inspectors view and switch to attribute inspector
Then select Render As: Template Image
This worked for me.

Resources