I'm trying to create an animation of a View coming into the screen from the bottom. But in the very first time it only appears on screen without any animation and then it starts work properly.
This is the code:
struct ContentView: View {
#State private var showView = false
var body: some View {
ZStack(alignment: .bottom){
VStack{
Button("TAP HERE") {
withAnimation(.spring()) {
showView.toggle()
}
}
Spacer()
}
if showView {
RoundedRectangle(cornerRadius: 30)
.frame(height: UIScreen.main.bounds.height * 0.5)
.transition(.move(edge: .bottom))
}
}
.edgesIgnoringSafeArea(.bottom)
}
}
This is the behavior:
What I'm doing wrong?
I'm using Xcode 14 beta 5 and Swift 5
try this:
if showView {
RoundedRectangle(cornerRadius: 30)
.frame(height: UIScreen.main.bounds.height * 0.5)
.transition(.move(edge: .bottom))
.zIndex(1) <-- here
}
Actually, i think that you just need to move the spring effect to the RoundedRectangle.
struct ContentView: View {
#State private var showView = false
var body: some View {
ZStack(alignment: .bottom){
VStack{
Button("TAP HERE") {
withAnimation {
showView.toggle()
}
}
Spacer()
}
if showView {
RoundedRectangle(cornerRadius: 30)
.frame(height: UIScreen.main.bounds.height * 0.5)
.transition(.move(edge: .bottom))
.animation(.spring(), value: showView)
}
}
.edgesIgnoringSafeArea(.bottom)
}
}
In Xcode the animation is a little strange, but in the simulator it works ok.
Related
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 :
I have a time picker in SwiftUI and any modification that I apply to the Text values in the picker are ignored. How do I do this correctly? I am using Xcode 13 on Big Sur.
Here is the result without any modification:
I want the times to be larger and white so I added .font() and .foregroundColor() modifiers. Here is my code:
struct TimerPicker: View {
#Binding var customTime: CustomTime
var body: some View {
GeometryReader { geometry in
HStack (spacing: 0.0){
VStack {
Picker(selection: self.$customTime.selectedHour, label: Text("Hrs")) {
ForEach(0..<24) { hour in
Text("\(hour) hrs")
.foregroundColor(.white)
.font(.title3)
}
}
}
.frame(width: geometry.size.width * 0.33)
.clipped()
VStack {
Picker(selection: self.$customTime.selectedMin, label: Text("Min")) {
ForEach(0..<61) { min in
Text("\(min) min")
.foregroundColor(.white)
.font(.title3)
}
}
}
.frame(width: geometry.size.width * 0.33)
.clipped()
VStack {
Picker(selection: self.$customTime.selectedSecond, label: Text("Sec")) {
ForEach(0..<61) { sec in
Text("\(sec) sec")
.foregroundColor(.white)
.font(.title3)
}
}
}
.frame(width: geometry.size.width * 0.33)
.clipped()
.transition(.scale)
}
} //: Geometry
}
}
There is no change in the styling of the picker values. What is the correct way to do this?
Your code seems to work for me, although I made the following small modifications.
This is the code that works for me:
(note there is no 60 min or 60 sec)
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
#State var customTime = CustomTime()
var body: some View {
Spacer()
TimerPicker(customTime: $customTime)
Spacer()
}
}
struct CustomTime: Identifiable, Hashable {
let id = UUID()
var selectedHour = 0
var selectedMin = 0
var selectedSecond = 0
}
struct TimerPicker: View {
#Binding var customTime: CustomTime
var body: some View {
GeometryReader { geometry in
HStack (spacing: 0.0){
VStack {
Picker(selection: $customTime.selectedHour, label: Text("Hrs")) {
ForEach(0..<24) { hour in
Text("\(hour) hrs").tag(hour) // <--- tag
.foregroundColor(.white)
.font(.title3)
}
}
}
.frame(width: geometry.size.width * 0.33)
.clipped()
VStack {
Picker(selection: $customTime.selectedMin, label: Text("Min")) {
ForEach(0..<60) { min in // <--- only to 59
Text("\(min) min").tag(min) // <--- tag
.foregroundColor(.white)
.font(.title3)
}
}
}
.frame(width: geometry.size.width * 0.33)
.clipped()
VStack {
Picker(selection: $customTime.selectedSecond, label: Text("Sec")) {
ForEach(0..<60) { sec in // <--- only to 59
Text("\(sec) sec").tag(sec) // <--- tag
.foregroundColor(.white)
.font(.title3)
}
}
}
.frame(width: geometry.size.width * 0.33)
.clipped()
.transition(.scale)
}
.pickerStyle(.wheel) // <--- here
.background(Color.red) // <--- here
} //: Geometry
}
}
I have a view that I'd like to completely cover at some point and I'd like just one particular child view not to be covered. Is this possible in SwiftUI?
See this code for example:
import SwiftUI
#main
struct ZIndexExperimentApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
#State var showCover = false
var body: some View {
ZStack {
VStack {
Spacer()
Text("Normal text")
.font(.headline)
.padding()
Text("Text that should always be visible")
.font(.headline)
.padding()
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
if self.showCover {
VStack {
Rectangle()
.fill(Color.blue)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.edgesIgnoringSafeArea(.all)
}
}
.onAppear(perform: {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.showCover = true
}
})
}
}
Is there a way to make the second Text to be on top of the cover? I tried to set the zindex on it to a high value but it didn't seem to have an effect.
I think using the foregroundColor or hidden is the better way, because if you kill some Views in your screen you would be notice some displacement on View which is not pleasant for user.
Version 1:
import SwiftUI
struct ContentView: View {
#State var showCover: Bool = Bool()
var body: some View {
ZStack {
if showCover { Color.blue }
VStack {
Spacer()
Text("Normal text")
.foregroundColor(showCover ? Color.clear : Color.primary)
.padding()
Text("Text that should always be visible")
.padding()
Spacer()
}
}
.font(.headline)
.ignoresSafeArea()
.onAppear() { DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) { showCover = true } }
}
}
Version 2:
import SwiftUI
struct ContentView: View {
#State var showCover: Bool = Bool()
var body: some View {
ZStack {
if showCover { Color.blue }
VStack {
Spacer()
if showCover { Text("Normal text").padding().hidden() }
else { Text("Normal text").padding() }
Text("Text that should always be visible")
.padding()
Spacer()
}
}
.font(.headline)
.ignoresSafeArea()
.onAppear() { DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) { showCover = true } }
}
}
I'm writing a widget with WidgetKit and I want to make the widget's content is clickable. For example, if users click to standings, I want to open the standings tab when the app becomes active.
I tried to use notification between the app and widget but the tap gesture is not working, I added print inside of the tap gesture but it did not appear in the console. Also, I added the same app group to both of them.
WidgetView:
struct LargeWidget : View {
#State var standings : [StandingsTable]
var body: some View {
VStack(alignment:.leading){
if standings.count > 0{
HStack(spacing:5){
Text("#").foregroundColor(.gray)
.frame(width: 30)
Text("Team".localized).foregroundColor(.gray)
Spacer()
Text("_D".localized).foregroundColor(.gray)
.frame(width: 30)
Text("_L".localized).foregroundColor(.gray)
.frame(width: 30)
Text("_W".localized).foregroundColor(.gray)
.frame(width: 30)
Text("_P".localized).foregroundColor(.gray)
.frame(width: 30)
}
Divider()
ForEach(0..<5, id: \.self) { i in
HStack(spacing:5){
Text(standings[i].rank)
.font(.system(size: 15))
.padding(.vertical, 3)
.frame(width: 30)
.background(Color(UIColor.systemBackground))
.cornerRadius(4)
Text(standings[i].name)
.lineLimit(1)
.padding(.leading, 5)
Spacer()
Text(standings[i].drawn)
.frame(width: 30)
Text(standings[i].lost)
.frame(width: 30)
Text(standings[i].won)
.frame(width: 30)
Text(standings[i].points)
.frame(width: 30)
}
.padding(.vertical, 5)
.background(standings[i].name == "Besiktas" ? Color(UIColor.systemGray6) : Color.clear)
.cornerRadius(8)
}
Spacer(minLength: 0)
}else{
Text("Large")
.padding()
}
}.padding()
.onTapGesture {
print("clicked to standings")
DispatchQueue.main.async(execute: {
NotificationCenter.default.post(name: NSNotification.Name("standings"), object: nil, userInfo: nil)
})
}
}
}
and here ContentView in app:
import SwiftUI
extension NSNotification {
static let openStandings = NSNotification.Name.init("standings")
}
struct ContentView: View {
#State var show: Bool = false
var body: some View {
NavigationView{
Text("Hello, world!")
.padding()
}.sheet(isPresented: self.$show) {
VStack{
Text("Notification")
.padding()
}
}
.onReceive(NotificationCenter.default.publisher(for: NSNotification.openStandings))
{ obj in
self.show.toggle()
}
}
}
Screenshot of Widget
Okay I created a new project with SwiftUI Environments (not old way AppDelegate and SceneDelegate).
Then I use Link for tap actions and I got it with .onOpenURL modifier in ContentView. It works :)
ContentView:
import SwiftUI
enum SelectedTab: Hashable {
case home
case standings
case options
}
struct ContentView: View {
#State var selectedTab: SelectedTab = .home
var body: some View {
TabView(selection: self.$selectedTab) {
Text("Home")
.tabItem {
Image(systemName: "house")
.renderingMode(.template)
Text("Home")
}.tag(SelectedTab.home)
Text("Standings")
.tabItem {
Image(systemName: "list.number")
.renderingMode(.template)
Text("Standings")
}.tag(SelectedTab.standings)
Text("Options")
.tabItem {
Image(systemName: "gear")
.renderingMode(.template)
Text("Options")
}.tag(SelectedTab.options)
.onOpenURL { (url) in
if url.absoluteString == "widget-deeplink://standings"{
self.selectedTab = .standings
}
}
}
}
}
Link usage example in Widget:
Link(destination: URL(string: "widget-deeplink://standings")!) {
Text("Link Test")
}
I need to make an Alert in SwiftUI that has an editable TextField in it. Currently, this isn't supported by SwiftUI (as of Xcode 11.3), so I'm looking for a work-around.
I know I can implement by wrapping the normal UIKit bits in a UIHostingController, but really want to stick with an all-SwiftUI implementation.
I've got two VStacks in a ZStack, with the front one (the one with the TextView) being hidden and disabled until until you tap the button. Take a look at this:
import SwiftUI
struct ContentView: View {
#State var isShowingEditField = false
#State var text: String = "12345"
var body: some View {
ZStack {
VStack {
Text("Value is \(self.text)")
Button(action: {
print("button")
self.isShowingEditField = true
}) {
Text("Tap To Test")
}
}
.disabled(self.isShowingEditField)
.opacity(self.isShowingEditField ? 0.25 : 1.00)
VStack(alignment: .center) {
Text("Edit the text")
TextField("", text: self.$text)
.multilineTextAlignment(.center)
.lineLimit(1)
Divider()
HStack {
Button(action: {
withAnimation {
self.isShowingEditField = false
print("completed... value is \(self.text)")
}
}) {
Text("OK")
}
}
}
.padding()
.background(Color.white)
.shadow(radius: CGFloat(1.0))
.disabled(!self.isShowingEditField)
.opacity(self.isShowingEditField ? 1.0 : 0.0)
}
}
}
This seems like it should work to me. Switching between the two VStacks works well, but the TextField is not editable.
It acts like it's disabled, but it's not. Explicitly .disabled(false) to the TextField doesn't help. Also, it should already be enabled anyway since 1) that's the default, 2) the VStack it's in is specifically being set as enabled, and 3) The OK button works normally.
Ideas/workarounds?
Thanks!
You need to force the TextField updating with some methods. Like the following:
TextField("", text: self.$text)
.multilineTextAlignment(.center)
.lineLimit(1)
.id(self.isShowingEditField)
That is really ridiculous, but the problem disappears if you comment just one line of code:
//.shadow(radius: CGFloat(1.0))
I commented it and everything works. I think you need to use ZStack somewhere in your custom alert view to avoid this.. hm, bug, maybe?
update
try some experiments. If you leave just this code in that View:
struct CustomAlertWithTextField: View {
#State var isShowingEditField = false
#State var text: String = "12345"
var body: some View {
TextField("", text: $text)
.padding()
.shadow(radius: CGFloat(1.0))
}
}
it would not work again. only if you comment .padding() or .shadow(...)
BUT if you relaunch Xcode - this code begin to work (which made me crazy). Tried it at Xcode Version 11.2 (11B52)
update 2 the working code version:
struct CustomAlertWithTextField: View {
#State var isShowingEditField = false
#State var text: String = "12345"
var body: some View {
ZStack {
VStack {
Text("Value is \(self.text)")
Button(action: {
print("button")
self.isShowingEditField = true
}) {
Text("Tap To Test")
}
}
.disabled(self.isShowingEditField)
.opacity(self.isShowingEditField ? 0.25 : 1.00)
ZStack {
Rectangle()
.fill(Color.white)
.frame(width: 300, height: 100)
.shadow(radius: 1) // moved shadow here, so it doesn't affect TextField now
VStack(alignment: .center) {
Text("Edit the text")
TextField("", text: self.$text)
.multilineTextAlignment(.center)
.lineLimit(1)
.frame(width: 298)
Divider().frame(width: 300)
HStack {
Button(action: {
withAnimation {
self.isShowingEditField = false
print("completed... value is \(self.text)")
}
}) {
Text("OK")
}
}
}
}
.padding()
.background(Color.white)
.disabled(!self.isShowingEditField)
.opacity(self.isShowingEditField ? 1.0 : 0.0)
}
}
}
after some research i solved this problem by adding .clipped() to the VStack.
For your problem, it would look like this:
VStack(alignment: .center) {
Text("Edit the text")
TextField("", text: self.$text)
.multilineTextAlignment(.center)
.lineLimit(1)
Divider()
HStack {
Button(action: {
withAnimation {
self.isShowingEditField = false
print("completed... value is \(self.text)")
}
}) {
Text("OK")
}
}
}
.padding()
.clipped() // <- here
.background(Color.white)
.shadow(radius: CGFloat(1.0))
.disabled(!self.isShowingEditField)
.opacity(self.isShowingEditField ? 1.0 : 0.0)