No fullScreenCover animation on close button - swiftui

I have a strange behavior.
When I close my fullScreenCover, I have no animation on it. On the other hand, I have the animation at the opening. I don't understand why I have no close animation.
Example codes
import SwiftUI
struct ContentView: View {
#EnvironmentObject var myViewModel: MyViewModel
private var gridItemLayout = [GridItem(.flexible()), GridItem(.flexible())]
var body: some View {
ScrollView {
LazyVGrid(columns: gridItemLayout, spacing: 10) {
ForEach(myViewModel.data, id: \.self) { data in
ChildView(data: data)
}
}
}
.onAppear() {
self.myViewModel.getData()
}
.navigationBarTitle("My ContentView")
}
}
import SwiftUI
struct ChildView: View {
#State var data: Data
#State var showModal = false
var body: some View {
Button(action: {
self.showModal.toggle()
}, label: {
VStack {
Text("\(data.name)")
.font(.body)
.frame(width: 180, height: 150/2, alignment: .bottom)
VStack {
Text("\(Image(systemName: "checkmark.bubble")) 15")
Text("\(Image(systemName: "clock")) 20'")
}
.frame(width: 180, height: 150/2, alignment: .bottomTrailing)
}
.cornerRadius(10)
.frame(minWidth: 0, maxWidth: 180, minHeight: 150, maxHeight: 150, alignment: .center)
})
.fullScreenCover(isPresented: $showModal) {
OtherView()
}
}
}
import SwiftUI
struct OtherView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
VStack {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
HStack {
Text("CLOSE")
Spacer()
Image(systemName: "checkmark")
.resizable()
.frame(width: 30, height: 30)
.padding()
}
}
}
.navigationBarTitle("My Modal", displayMode: .inline)
}
}
}
Thanks.

Related

On appear doesn't trigger every time

I have problem because on appear modifier doesn't call every time when I enter the screen and it calls randomly (when on appear is applied to navigation view). When I put it on some view inside navigation view it never calls. What is the problem, I can't solve it, this is very strange behaviour. This is the view:
struct CheckListView: View {
#EnvironmentObject var appState: AppState
#StateObject var checkListViewModel = CheckListViewModel()
//#State var didSelectCreateNewList = false
#State var didSelectShareList = false
var body: some View {
NavigationView {
GeometryReader { reader in
VStack {
Spacer()
.frame(height: 30)
Text("\(appState.hikingTypeModel.name)/\(appState.lengthOfStayType.name)")
.foregroundColor(Color("rectBackground"))
.multilineTextAlignment(.center)
.font(.largeTitle)
Spacer()
.frame(height: 40)
List {
ForEach(checkListViewModel.checkListModel.sections) { section in
CheckListSection(model: section)
.listRowBackground(Color("background"))
}
}
.listStyle(.plain)
.clearListBackground()
.clipped()
Spacer()
.frame(height: 40)
HStack {
FilledRectangleBorderButtonView(titleLabel: "Share", backgroundColor: Color("rectBackground"),foregroundColor: .white, height: 55, didActionOnButtonHappened: $didSelectShareList)
Spacer()
FilledRectangleBorderButtonView(titleLabel: "Create new list", backgroundColor: .clear, foregroundColor: Color("rectBackground"), height: 55, didActionOnButtonHappened: $checkListViewModel.didSelectNewList)
.onChange(of: checkListViewModel.didSelectNewList) { _ in
UserDefaultsHelper().emptyUserDefaults()
appState.moveToRootView = .createNewList
}
}
.padding([.leading, .trailing], 20)
.frame(width: reader.size.width)
Spacer()
}
.frame(width: reader.size.width, height: reader.size.height)
.background(Color("background"))
.navigationBarStyle()
}
}
.onAppear {
self.checkListViewModel.loadJSON(hikingTypeId: appState.hikingTypeModel.id, lengthStayId: appState.lengthOfStayType.id)
}
}
}

SwiftUI: Double picker wheels with system behavioral

I want to recreate system picker behavioral with two options in wheels with SwiftUI and faced ton of problem. Some of this I solved but some still unsolved. I have pop-ups with different views inside. One of the view it's a DatePicker with displayedComponents: .hourAndMinute. And other one is two Pickers inside HStack. My question is how to make Pickers make look like in system: without white spacing between?
struct MultyPicker: View {
#State var value = 1
#State var value2 = 1
var body: some View {
ZStack(alignment: .bottom) {
Color.black.opacity(0.5)
ZStack {
VStack {
Text("Header")
.font(.title3)
.fontWeight(.bold)
HStack(spacing: 0) {
Picker(selection: $value, label: Text("")) {
ForEach(1..<26) { number in
Text("\(number)")
.tag("\(number)")
}
}
.pickerStyle(WheelPickerStyle())
.compositingGroup()
.clipped(antialiased: true)
Picker(selection: $value2, label: Text("")) {
ForEach(25..<76) { number in
Text("\(number)")
.tag("\(number)")
}
}
.pickerStyle(WheelPickerStyle())
.compositingGroup()
.clipped(antialiased: true)
}
}
.padding()
.background(
RoundedRectangle(cornerRadius: 34)
.foregroundColor(.white)
)
}
.padding(.horizontal)
.padding(.bottom, 50)
}
.edgesIgnoringSafeArea([.top, .horizontal])
}
}
// This extension for correct touching area
extension UIPickerView {
open override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: super.intrinsicContentSize.height)
}
}
Want to achive looks like that with one grey line in selected value
//
// Test2.swift
// Test
//
// Created by Serdar Onur KARADAĞ on 26.08.2022.
//
import SwiftUI
struct Test2: View {
#State var choice1 = 0
#State var choice2 = 0
var body: some View {
ZStack {
Rectangle()
.fill(.gray.opacity(0.2))
.cornerRadius(30)
.frame(width: 350, height: 400)
Rectangle()
.fill(.white.opacity(1))
.cornerRadius(30)
.frame(width: 300, height: 350)
VStack {
Text("HEADER")
HStack(spacing: 0) {
Picker(selection: $choice1, label: Text("C1")) {
ForEach(0..<10) { n in
Text("\(n)").tag(n)
}
}
.pickerStyle(.wheel)
.frame(minWidth: 0)
.clipped()
Picker(selection: $choice2, label: Text("C1")) {
ForEach(0..<10) { n in
Text("\(n)").tag(n)
}
}
.pickerStyle(.wheel)
.frame(minWidth: 0)
.clipped()
}
}
}
}
}
struct Test2_Previews: PreviewProvider {
static var previews: some View {
Test2()
}
}
SwiftUI multi-component Picker basically consists of several individual Picker views arranged horizontally. Therefore, we start by creating an ordinary Picker view for our first component. I am using Xcode version 13.4.1(iOS 15.0).
import SwiftUI
struct ContentView: View {
#State var hourSelect = 0
#State var minuteSelect = 0
var hours = [Int](0..<24)
var minutes = [Int](0..<60)
var body: some View {
ZStack {
Color.black
.opacity(0.5)
.ignoresSafeArea()
.preferredColorScheme(.light)
Rectangle()
.fill(.white.opacity(1))
.cornerRadius(30)
.frame(width: 300, height: 350)
VStack {
Text("Header")
HStack(spacing: 0) {
Picker(selection: $hourSelect, label: Text("")) {
ForEach(0..<self.hours.count) { index in
Text("\(self.hours[index])").tag(index)
}
}
.pickerStyle(.wheel)
.frame(minWidth: 0)
.compositingGroup()
.clipped()
Picker(selection: $minuteSelect, label: Text("")) {
ForEach(0..<self.minutes.count) { index in
Text("\(self.minutes[index])").tag(index)
}
}
.pickerStyle(.wheel)
.frame(minWidth: 0)
.compositingGroup()
.clipped()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Output :

Why the scrollview doesn't get updated with new data from array?

I'm trying to send and then display them in the scrollview realtime. But nothing shows up. How to solve it? So, basically when the user types the message into a textbox then it will be saved in array and then it will be populated to the crollView in realtime so the user can view all the messages.
Error: No errors, it just isn't visible.
import SwiftUI
struct SingleMessageBubbleModel: Identifiable {
let id = UUID()
var text: String
var received: Bool
var timeStamp: Date
}
var messagesDBArray : [SingleMessageBubbleModel] = []
struct ContentView: View {
#State private var showOnTheSpotMessaging: Bool = true
#State var textTyped: String
var body: some View {
if (showOnTheSpotMessaging) {
VStack {
HStack {
ScrollViewReader { proxy in
ScrollView {
LazyVStack {
ForEach(messagesDBArray, id: \.id) { message in
MessageBubble(message: message)
}
}
}
.padding(.top, 10)
.background(.gray)
.onChange(of: messagesDBArray.count) { id in
withAnimation {
proxy.scrollTo(id, anchor: .bottom)
}
}
}
.frame( height: 200, alignment: .bottomLeading)
}
HStack () {
TextEditor (text: $textTyped)
.frame(width: 200, height: 200, alignment: .leading)
Button ("Send", action: {
messagesDBArray.append(SingleMessageBubbleModel(text: textTyped, received: true, timeStamp: Date()))
})
}
}
}
}
}
struct MessageBubble: View {
var message: SingleMessageBubbleModel
#State private var showTime = false
var body: some View {
VStack(alignment: message.received ? .leading : .trailing) {
HStack {
Text(message.text)
.padding()
.background(message.received ? Color.gray : Color.blue)
.cornerRadius(30)
}
.frame(maxWidth: 300, alignment: message.received ? .leading : .trailing)
.onTapGesture {
withAnimation {
showTime.toggle()
}
}
if showTime {
Text("\(message.timeStamp.formatted(.dateTime.hour().minute()))")
.font(.caption2)
.foregroundColor(.gray)
.padding(message.received ? .leading : .trailing, 25)
}
}
.frame(maxWidth: .infinity, alignment: message.received ? .leading : .trailing)
.padding(message.received ? .leading : .trailing)
.padding(.horizontal, 4)
}
}
Basically, when the button is pressed, your property messagesDBArray is well and truly append with the new value.
However, and it's really important to understand this point in swiftUI, nothing triggers the refresh of the view.
I suggest you two solutions:
If you don't need messagesDBArray to be outside of ContentView:
You just have to add messagesDBArray as a state in ContentView like following
struct ContentView: View {
#State var messagesDBArray : [SingleMessageBubbleModel] = []
#State private var showOnTheSpotMessaging: Bool = true
#State var textTyped: String = ""
var body: some View {
if (showOnTheSpotMessaging) {
VStack {
HStack {
ScrollViewReader { proxy in
ScrollView {
LazyVStack {
ForEach(messagesDBArray, id: \.id) { message in
MessageBubble(message: message)
}
}
}
.padding(.top, 10)
.background(.gray)
.onChange(of: messagesDBArray.count) { id in
withAnimation {
proxy.scrollTo(id, anchor: .bottom)
}
}
}
.frame( height: 200, alignment: .bottomLeading)
}
HStack () {
TextEditor (text: $textTyped)
.frame(width: 200, height: 200, alignment: .leading)
Button ("Send", action: {
messagesDBArray.append(SingleMessageBubbleModel(text: textTyped, received: true, timeStamp: Date()))
})
}
}
}
}
}
If you need messagesDBArray to be outside of ContentView:
1- Create a class (ViewModel or Service or whatever you wan to call it) with messagesDBArray as a #Published property
final class ViewModel: ObservableObject {
#Published var messagesDBArray : [SingleMessageBubbleModel] = []
}
2- Observe this class in ContentView in order to append and receive the update
struct ContentView: View {
#ObservedObject private var viewModel = ViewModel()
#State private var showOnTheSpotMessaging: Bool = true
#State var textTyped: String = ""
var body: some View {
if (showOnTheSpotMessaging) {
VStack {
HStack {
ScrollViewReader { proxy in
ScrollView {
LazyVStack {
ForEach(viewModel.messagesDBArray, id: \.id) { message in
MessageBubble(message: message)
}
}
}
.padding(.top, 10)
.background(.gray)
.onChange(of: viewModel.messagesDBArray.count) { id in
withAnimation {
proxy.scrollTo(id, anchor: .bottom)
}
}
}
.frame( height: 200, alignment: .bottomLeading)
}
HStack () {
TextEditor (text: $textTyped)
.frame(width: 200, height: 200, alignment: .leading)
Button ("Send", action: {
viewModel.messagesDBArray.append(SingleMessageBubbleModel(text: textTyped, received: true, timeStamp: Date()))
})
}
}
}
}
}
I hope that this is clear to you and that it has been useful 😉

SwiftUI - How to resize PickerView?

How do you resize the picker view in SwiftUI? I need to change the width that it takes up. My code below is just a simple view with a picker inside it. Changing the width parameter does not change the width of the picker view.
struct CalibrationBar: View {
#State var tone = Int()
var body: some View{
HStack{
Button(action: {
playTone(tone: self.tone, amp: 50, stop: true)
}) {
Text("50 dB")
}
.frame(width: 60.0)
Picker(selection: .constant(1), label: Text("")) {
Text("+0").tag(1)
Text("+2").tag(2)
Text("+4").tag(3)
}
.clipped()
.frame(minWidth: 0, maxWidth: 100)
.labelsHidden()
}
}
}
struct ContentView: View {
var body: some View{
HStack{
Button(action: {
}) {
Text("50 dB")
}
.frame(width: 60.0)
VStack {
Picker(selection: .constant(1), label: Text("")) {
Text("+0").tag(1)
Text("+2").tag(2)
Text("+4").tag(3)
}
//.clipped()
.frame(width: 50)
.clipped()
}.border(Color.red)
}
}
}

Using SwiftUI. My Slider/Side-menu launches new Views just fine when clicked but click <back> button and now all the options are 'dead'

Using SwiftUI and a slider/side menu tutorial that I have augmented in order to put actions on each of the side menu selections.
When the side menu is displayed and I tap a menu option, it works great and takes me to a new view with a menu item. But when i tap on and see the side menu still in place, all the menu items are not dead. The menu items still animate a click (with a flicker) but nothing happens. I have to close the side menu, reopen it, and then the menu items work once again - one time.
Can anyone tell me why this is happening?
Here is the pretty contentview, the mainview, and the sidemenu view.
//ContentView.swift
import SwiftUI
struct ContentView: View {
#State var showMenu = false
var body: some View {
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
withAnimation {
self.showMenu = false
}
}
}
return NavigationView {
GeometryReader { geometry in
ZStack(alignment: .leading) {
MainView(showMenu: self.$showMenu)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(x: self.showMenu ? geometry.size.width/2 : 0)
.disabled(self.showMenu ? true : false)
if self.showMenu {
MenuView()
.frame(width: geometry.size.width/2)
.transition(.move(edge: .leading))
}
}
.gesture(drag)
}
.navigationBarTitle("Side Menu", displayMode: .inline)
.navigationBarItems(leading: (
Button(action: {
withAnimation {
self.showMenu.toggle()
}
}) {
Image(systemName: "line.horizontal.3")
.imageScale(.large)
}
))
}
}
}
struct MainView: View {
#Binding var showMenu: Bool
var body: some View {
Button(action: {
withAnimation {
self.showMenu = true
}
}) {
Text("Show Menu")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and here is the sidemenu view.
//MenuView.swift
import SwiftUI
struct PlayerView: View {
#State var showMenu = true
//#EnvironmentObject var session: SessionStore
var body: some View {
VStack{
//self.showMenu = true
Text("Manage Players Here").foregroundColor(.red)
}
}
}
struct MenuView: View {
#State var showMenu = true
var body: some View {
VStack(alignment: .leading) {
HStack() {
NavigationLink(destination: PlayerView()) {
HStack(){
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Players")
.foregroundColor(.gray)
.font(.headline)
}
}
}
.padding(.top, 100)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(.all)
}
}
struct MenuView_Previews: PreviewProvider {
static var previews: some View {
MenuView()
}
}
enter code here
1) Binding var in the MenuView
2) OnAppear{} with Zstack to turn off the showMenu
struct ContentView: View {
#State var showMenu = false
var body: some View {
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
withAnimation {
self.showMenu = false
}
}
}
return NavigationView {
GeometryReader { geometry in
ZStack(alignment: .leading) {
MainView(showMenu: self.$showMenu)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(x: self.showMenu ? geometry.size.width/2 : 0)
.disabled(self.showMenu ? true : false)
if self.showMenu {
MenuView(showMenu: self.$showMenu)
.frame(width: geometry.size.width/2)
.transition(.move(edge: .leading))
}
}
.gesture(drag).onAppear {
self.showMenu = false
}
}
.navigationBarTitle("Side Menu", displayMode: .inline)
.navigationBarItems(leading: (
Button(action: {
withAnimation {
self.showMenu.toggle()
}
}) {
Image(systemName: "line.horizontal.3")
.imageScale(.large)
}
))
}
}
}
struct MainView: View {
#Binding var showMenu: Bool
var body: some View {
Button(action: {
withAnimation {
self.showMenu = true
}
}) {
Text("Show Menu")
}
}
}
struct PlayerView: View {
#State var showMenu = true
//#EnvironmentObject var session: SessionStore
var body: some View {
VStack{
//self.showMenu = true
Text("Manage Players Here").foregroundColor(.red)
}
}
}
struct MenuView: View {
#Binding var showMenu: Bool // = true
var body: some View {
VStack(alignment: .leading) {
HStack() {
NavigationLink(destination: PlayerView()) {
HStack(){
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Players")
.foregroundColor(.gray)
.font(.headline)
}
}
}
.padding(.top, 100)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(.all)
}
}