How to find out if view is currently selected in SwiftUI - ios

I'd like to find out whether my view is currently selected or not.
I have a rectangle which represents a card. Once I tap the card, the card should go into the selected state and change properties like the color.
But once I tap somewhere else, the card should not be in that state anymore.
So far, I did not manage to figure this out as I tried with the onTapGesture property but with that I run into the problem that when I tap outside that card, the card does not change the state to false unless I tap the card again, which makes sense but it seems to be the wrong choice in this case.
import SwiftUI
struct CardView: View {
#State var selected = false
let rectangle = Rectangle()
#State var rectangleColor: Color = .purple
var body: some View {
GeometryReader { g in
self.rectangle
.foregroundColor(self.rectangleColor)
.frame(width: g.size.width / 2, height: g.size.width / 2)
.onTapGesture {
self.selected.toggle()
self.modifyColors()
}
}
}
func modifyColors() {
if selected {
rectangleColor = .red
} else {
rectangleColor = .purple
}
}
}
The selected state is the red color, the unselected one the purple color.
So I want that the color becomes red when I tap into the rectangle but not if I tap outside it.
It should become purple only again when I tap outside the rectangle but not inside it.
Example: Card is purple. I select it and it becomes red. When I then tap it again, it should stay red. It should become only purple (unselected) when I tap somewhere outside the card but not inside it.
View that contains this rectangle:
import SwiftUI
struct ContentView: View {
var body: some View {
CardView()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
It shouldn't matter where the view is actually - I want to know weather the space around that view is tapped. So if it is inside a modal, or the top view on a ZStack, the behavior should be the same.

The way to achieve this is by having the parent view manage both the selection and the gestures.
The CardView just takes a #Binding to isSelected and changes the colour accordingly:
struct CardView: View {
#Binding var isSelected: Bool
var body: some View {
Rectangle()
.fill(self.isSelected ? Color.red : Color.purple)
.aspectRatio(1.0, contentMode: .fit)
}
}
While the parent manages the #State and updates it using gestures.
struct ContentView: View {
#State var isCardSelected = false
var body: some View {
ZStack {
Color.white
.onTapGesture {
self.isCardSelected = false
}
CardView(isSelected: $isCardSelected)
.onTapGesture {
self.isCardSelected = true
}
}
}
}

Related

alternative for arrowEdge of swiftui popover for iOS

Is it possible for iPadOS to control the position of the popover, to appear on the right side of the view, or under, basically the same behaviour that arrowEdge is offering for macOS.
Right now it is always placed above of the view, like in the picture below. I would like to place it on the right side of the view, where the "red" popover is placed
The code:
struct PopoverExample: View {
#State private var isShowingPopover = false
var body: some View {
Button("Show Popover") {
self.isShowingPopover = true
}
.popover(isPresented: $isShowingPopover, arrowEdge: .trailing) {
Text("Popover Content")
.padding()
}
}
}

Changing background color based on another View in SwiftUI?

I have WheelView and WheelBackground, first one is a group of rounded rectangles that i rotate with a drag gesture based on degrees, second one is the background, changing its color based on degrees too, i would like to have these background colors like my squares, for example red square, red back, green square green back and so on...but i don't know how to change these colors based on degrees, any suggestions?
This is my code:
struct WheelBackground: View {
#Binding var degree: Double
var body: some View {
Color.orange
.opacity(0.4)
.ignoresSafeArea()
.hueRotation(Angle(degrees: degree))
}
}
struct ContentView: View {
#State var degree = -90.0
var body: some View {
ZStack {
WheelBackground(degree: $degree)
WheelView(degree: $degree)
.rotationEffect(Angle(degrees: degree))
}
}
}
Refer to your question and comment, since you could not provide the code of WheelView(), this is the closest replicated view that I can make to demonstrate the solution for your problem. This code focuses on the logic that you want your background and square to have the same color whenever the color changes.
Try this below code:
struct TestContentView: View {
let allColors = [Color.red, Color.blue, Color.green, Color.orange, Color.purple, Color.pink]
#State var color = Color.red
var body: some View {
ZStack {
WheelBackgroundExample(color: $color)
VStack {
WheelViewExample(color: $color)
Button("change") {
color = allColors[Int.random(in: 0...5)]
}
.foregroundColor(.black)
}
}
}
}
struct WheelViewExample: View {
#Binding var color: Color
var body: some View {
Rectangle()
.fill(color)
.frame(width: 200, height: 200)
.border(.black)
}
}
struct WheelBackgroundExample: View {
#Binding var color: Color
var body: some View {
Rectangle()
.fill(color)
}
}

Present modal view on navigation to another view in swiftui

Let me know if this is a duplicate question.
I am developing an app with a photo picker. I am on view A and there is a + button on it. When I tap on the +, I want to navigate to a view B. Upon navigation I want to present a Photo Picker view automatically inside view B. I am not able to figure out how to do that presentation of the sheet in Swiftui.
In UIKit,on the viewdidappear of viewcontroller B, i would present the Pickerview.
Here's the code that I have
```import SwiftUI
struct ViewB: View {
#State private var showingImagePicker = false
#State private var uploadedPhotos = [UIImage]()
var body: some View {
VStack {
Button("Select Image") {
showingImagePicker = true
}
}
.sheet(isPresented: $showingImagePicker) {
PhotoPicker(photoList: $uploadedPhotos)
}
.onChange(of: uploadedPhotos) { _ in
}
}
}```
This code is ViewB and I will present the pickerView on tapping a button in viewB. but I don't want to tap on button but instead want the picker to show up on appear of ViewB
There is a view modifier in SwiftUI known as .onAppear(perform:), which is the SwiftUI counterpart to UIKit's viewDidAppear and gives you an entry point upon the construction and display of a SwiftUI view. Simply add this modifier to View B (the view inside the sheet that you are presenting). In the closure that you provide to the modifier, you can change the state to present the picker as needed.
If you'd like the picker view to animate in after the view appears, the appropriate place to declare the transition and animation context is on the view acting as your picker view.
struct ViewB: View {
#State private var displayPicker = false
var body: some View {
VStack {
Text("This is View B")
if displayPicker {
PickerView()
.transition(.slide)
.animation(.easeInOut, value: displayPicker)
}
}
.onAppear {
displayPicker = true
}
}
}
Read more about the modifier here:
https://developer.apple.com/documentation/SwiftUI/AnyView/onAppear(perform:)
Think I figured it out -
struct CreateView: View {
#State private var image: Image?
#State private var showingImagePicker = false
#State private var uploadedPhotos = [UIImage]()
var body: some View {
ScrollView {
HStack {
image?.resizable().scaledToFit()
}
.onAppear {
showingImagePicker = true
}
.sheet(isPresented: $showingImagePicker) {
PhotoPicker(photoList: $uploadedPhotos)
}
.onChange(of: uploadedPhotos) { _ in
guard let uiImage = uploadedPhotos.first else {return}
image = Image(uiImage: uiImage)
}
}
}
}
Here PhotoPicker() is the customview that I have

SwiftUI - Detect Tap Outside View or Stack

Is there is a way to detect a tap outside View/HStack/VStack in SwiftUI?
I have tried to search stack overflow for a similar question, but all I could find are questions about how to dismiss the keyboard.
I have also tried to search the available HSTACK methods, and couldn't find any specific method handling click outside the view.
I believe the answer here will work for you: Detect touch outside Button in SwiftUI
struct ContentView: View {
var onConfirmPress: () -> Void
#State private var tappedOutsideView: Bool = false
var body: some View {
GeometryReader { geometry in
ZStack {
// a transparent rectangle under everything
Rectangle()
.frame(width: geometry.size.width, height: geometry.size.height)
.opacity(0.001) // <--- important
.layoutPriority(-1)
.onTapGesture {
self.tappedOutsideView = true
}
// your view
}
}
}
}

How to automatically detect NavigationBarTitle displayMode become .inline or .large in SwiftUI?

I have a List inside a navigationView and I want to change the List Section text whenever the list is scrolled and the navigationBarTitle become .inline or .large
This is my code:
import SwiftUI
struct ContentView: View {
#State private var scrolledUp = false
var body: some View {
NavigationView {
if scrolledUp {
List {
Section(header: Text("Moved UP"))
{
Text("Line1").bold()
Text("Line2").bold()
Text("Line2").bold()
}
.navigationBarTitle("Setting")
}
} else {
List {
Section(header: Text("Not Moved"))
{
Text("Line1").bold()
Text("Line2").bold()
Text("Line2").bold()
}
}
.navigationBarTitle("Setting")
}
}
}
}
how can I find out the list is scrolled and the navigationBar is changed to .title?
For now I used geometryReader to detect the y position of the List section header. During the scroll. If the position is less than 80, the navigationBarTitle is changed to .title.
It works perfectly. However, I still looking for a better solution.

Resources