У меня проблема с Xcode: переменная отображается как необязательная, хотя это не так.
Сначала я получаю ошибку в Xcode:
Значение необязательного типа «Необязательно» должно быть развернуто, чтобы ссылаться на элемент «timeFromDate» обернутого базового типа «Дата».
Поэтому я добавляю оператор !
, а затем Xcode выдает следующую ошибку:
Невозможно принудительно развернуть значение необязательного типа «Дата».
Вот мой код:
import Foundation
import SwiftUI
struct DayView: View {
@State var appointments: [Appointment] = []
@State var dates = [
Date(),
Calendar.current.date(byAdding: .minute, value: 30, to: Date()),
Calendar.current.date(byAdding: .hour, value: 60, to: Date()),
Calendar.current.date(byAdding: .hour, value: 90, to: Date()),
Calendar.current.date(byAdding: .hour, value: 120, to: Date())
]
@State var selectedDate: Date?
var date: Date
var body: some View {
ScrollView {
VStack {
Text(date.dayOfWeek())
Text(date.fullMonthDayYearFormat())
Divider().padding(.vertical)
Text("Select a Time")
.font(.largeTitle)
.bold()
Text("Duration: 30 Minutes")
ForEach(dates, id: \.self) { date in
HStack {
Button {
withAnimation {
selectedDate = date
}
} label: {
Text(date!.timeFromDate())
.bold()
.padding()
.frame(maxWidth: .infinity)
.foregroundColor(selectedDate == date ? .white : .blue)
.background(
ZStack {
if selectedDate == date {
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.gray)
} else {
RoundedRectangle(cornerRadius: 10).stroke()
}
}
)
}
if selectedDate == date {
NavigationLink {
BookingView(resident: Resident(firstName: "Andrew", middleName: "Warren Alexander", lastName: "Higbee", phoneNumber: "3852218786", address: "343 E 100 N Apt 12, Provo, UT, 84606", rentAmount: 800, pastDueRentOwed: 200, isPastDue: true, monthlyReminderScheduled: true, house: "Lion's Den", roomNumber: 3, bedNumber: 1, housePin: 7539, moveInDate: "2/9/2023"), date: date)
} label: {
Text("Next")
.bold()
.padding()
.frame( maxWidth: .infinity)
.foregroundColor(.white)
.background(
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.blue)
)
}
}
}
}
.padding(.horizontal)
}
}
.navigationTitle("Saturday")
.navigationBarTitleDisplayMode(.inline)
.onAppear(perform: {
dates = timesWith30MinuteIncrements(for: date)
})
}
func timesWith30MinuteIncrements(for date: Date) -> [Date] {
let calendar = Calendar.current
let startDate = calendar.date(bySettingHour: 8, minute: 0, second: 0, of: date)!
let endDate = calendar.date(bySettingHour: 17, minute: 0, second: 0, of: date)!
var times: [Date] = []
var currentDate = startDate
while currentDate <= endDate {
times.append(currentDate)
guard let newDate = calendar.date(byAdding: .minute, value: 30, to: currentDate) else { break }
currentDate = newDate
}
return times
}
}
Проблемная строка — Text(date.timeFromDate()).
Есть идеи?
Обновлено: я перестроил все приложение с нуля, скопировав и вставив. Я сократил код, чтобы изолировать проблему. В той же строке Text(date.timeFromDate())
дата заполняется как необязательная, и код строится таким образом.
Почему моя переменная необязательна?
Вот урезанный код:
import Foundation
import SwiftUI
struct DayView: View {
@State var dates = [
Date(),
Calendar.current.date(byAdding: .minute, value: 30, to: Date()),
Calendar.current.date(byAdding: .hour, value: 60, to: Date()),
Calendar.current.date(byAdding: .hour, value: 90, to: Date()),
Calendar.current.date(byAdding: .hour, value: 120, to: Date())
]
@State var date: Date
var body: some View {
ScrollView {
VStack {
Text("August 29, 2023")
Divider()
.padding(.vertical)
Text("Select a Time")
.font(.largeTitle)
.bold()
Text("Duration: 30 minutes")
ForEach(dates, id: \.self) { date in
Button {
} label: {
Text(date!.timeFromDate())
}
}
}
}
.navigationTitle("Saturday")
.navigationBarTitleDisplayMode(.inline)
.onAppear {
dates = timesWith30MinuteIncrements(for: date)
}
}
func timesWith30MinuteIncrements(for date: Date) -> [Date] {
let calendar = Calendar.current
let startDate = calendar.date(bySettingHour: 8, minute: 0, second: 0, of: date)!
let endDate = calendar.date(bySettingHour: 18, minute: 0, second: 0, of: date)!
var times: [Date] = []
var currentDate = startDate
while currentDate <= endDate {
times.append(currentDate)
guard let newDate = calendar.date(byAdding: .minute, value: 15, to: currentDate) else { break }
currentDate = newDate
}
return times
}
}
#Preview {
NavigationStack {
DayView(date: Date())
}
}
Вот моя функция timeFromDate:
func timeFromDate() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm a"
return formatter.string(from: self)
}
Что такое timeFromDate()
? Это тот, который возвращает необязательное значение? Вы имели в виду date.timeFromDate()!
вместо date!.timeFromDate()
?
func timeFromDate() -> String { let formatter = DateFormatter() formatter.dateFormat = "чч:мм a" return formatter.string(from: self) }
Я испробовал все эти варианты с оператором взрыва. Он продолжает переключаться между ошибками, потому что это необязательно, и ошибкой, потому что это не является необязательным.
func dayOfWeek() -> String { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "EEEE" return dateFormatter.string(from: self) }
Покажите нам код, который действительно воспроизводит проблему. Сейчас у нас недостаточно информации.
Какую еще информацию я могу предоставить?
Проблема, которую вы видите, связана не с @State var date
, который не является обязательным, а с вводом даты в замыкание ForEach, т.е.
ForEach(dates, id: \.self) { date in //<<< this one
Button {
} label: {
Text(date!.timeFromDate())
}
}
Это элемент вашего массива dates
. Несколько элементов этого массива создаются с помощью метода Calendar.current.date(byAdding: value:)
, который возвращает необязательную дату, поэтому массив имеет подпись var dates: [Optional<Date>]
. Вот почему компилятор говорит, что дату необходимо развернуть перед использованием.
если вы выберете вариант, нажмите .date(byAdding:
, вы увидите, что он возвращает необязательную дату Date?
.
(нажатие на опцию dates
также покажет, что она не является обязательной, и вы получите сообщение об ошибке, если скажете Swift, что она не должна быть необязательной dates: [Date]
)
Это означает, что тип вашего массива дат на самом деле является массивом необязательных дат [Date?]
Так что ваш date
в ForEach(dates, id: \.self) { date in
на самом деле необязателен.
Несколько способов справиться с этим в зависимости от ваших намерений,
Вы можете обернуть кнопку (или просто метку) if let date {
Вы можете сжать массив дат по умолчанию с помощью .compactMap { $0 }
Или вы можете выполнить логику где-то еще и передать массив необязательных значений в качестве начального значения при использовании DayView(dates = someNonOptionalDates).
Я думаю, что компилятор был чем-то сбит с толку, сообщив вам, что принудительное развертывание недоступно. Вероятно, это связано с вашим методом timeFromDate
. (он не включен в пример кода, поэтому мне пришлось удалить его для тестирования). Вместо этого я использовал Text(date!, format: .dateTime)
, и это сработало нормально.
Завершенное примечание: очевидно, понятия не имею, что делает ваш метод, но, возможно, стоит посмотреть Text
в таком формате, как if let date { Text(date, format: .dateTime.hour().minute()) }
, если вы просто хотите показать время, он справится с большой сложностью и локализацией для вас!
редактировать -
Другой способ, поскольку вы уже защищаете от необязательной даты в другой логике даты, — это переместить ее в расширение. например. грубо:
extension Date {
func thirtyMinuteIncrements(startHour: Int = 8, endHour: Int = 18) -> [Date] {
...
let startDate = calendar.date(bySettingHour: startHour, minute: 0, second: 0, of: self)!
...
}
}
Что может примерно сделать ваши onAppear и State
@State var dates = Date.now.thirtyMinuteIncrements()
.onAppear { dates = date.thirtyMinuteIncrements() }
с дополнительным преимуществом, что ваше состояние больше не является набором дополнительных опций, это просто [Date]
Я вижу ошибку. Значение типа «Дата» не имеет члена «dayOfWeek». Как реализованы расширения
Date.dayOfWeek
иDate.timeFromDate
?