Remove Blurred Preview for ContextMenu on NavigationLink in SwiftUI - swift

I'm trying to add a contextMenu to a NaviagationLink in swiftUI. The result as you can see below is a blurred view is showing as a preview.
NavigationLink {
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
} label: {
Text(item.timestamp!, formatter: itemFormatter)
.padding()
}
.contextMenu(
ContextMenu {
Button(action: {
print("")
}) {
Label("Save", systemImage: "square.and.arrow.up")
}
}
)
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()

I put together some example code to see if I could understand the problem. It's the underlying text in your view which gets blurred by the ContextMenu action. If the items are sufficiently spaced I am not seeing the blurring issue. Here is the code example I set up.
struct ItemWithTimeStamp: Identifiable, Hashable {
var id: UUID
let timestamp: Date
let name: String
}
struct ContentView: View {
let examples = [ItemWithTimeStamp(id: UUID(), timestamp: Date(), name: "Thing One"), ItemWithTimeStamp(id: UUID(), timestamp: Date(timeInterval: 1234, since: .now), name: "Thing Two"), ItemWithTimeStamp(id: UUID(), timestamp: Date(timeInterval: 6543, since: .now), name: "Thing Three")]
var body: some View {
NavigationView {
VStack {
ForEach(examples, id: \.id) { item in
NavigationLink {
Text("Item at \(item.timestamp, formatter: itemFormatter)")
} label: {
Text(item.timestamp, formatter: itemFormatter)
.padding()
}
.contextMenu(
ContextMenu {
Button(action: {
print("Saved")
}) {
Label("Save", systemImage: "square.and.arrow.up")
}
}
)
}
}
}
}
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
}
This is the View before activating the context menu.
Here I'm activating the ContextMenu and you can see the blurred text in the background. But in my case with standard .padding() between the items the overlap isn't happening so I'm not seeing the blurring issue as you're getting. But I'm not sure what the rest of your view looks like.
I don't know if it suits your purposes but you might try placing the NavigationLink's in a List as they are definitely separated out and will not overlap. Replace the VStack with List in my code to see an example of this.

Related

Is it possible to display saved DatePicker inputs to another view

I am trying to return DatePicker inputs which is saved to core data, from details view to another view. But nothing is being returned.
Where am I going wrong?
truct ProductCard: View {
#Environment (\.managedObjectContext) var managedObjContext
#FetchRequest(entity: Task.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Task.name, ascending: true)])
var body: some View {
ScrollView(.horizontal){
HStack{
ForEach( Task, id: \.self){ Task in
NavigationLink {
ProductCardDetails(taskData: Task)
} label: {
HStack{
Text(task.self.taskTitle ?? "Error")
.font(.title3)
.fontWeight(.bold)
.foregroundColor(.red)
Spacer()
// this should be Date right?
Text(task.self.taskDate ?? "Error")
.font(.caption)
.foregroundColor(.red)
}.padding(.horizontal, 10)
}
}
}
}
}
}
// where the inputs will be put in
struct ProductCardDetails: View {
let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-dd-MM HH:mm:ss"
return formatter
}()
#Environment(\.managedObjectContext) var managedObjContext
#Environment(\.dismiss) var dismiss
var taskData: Task
#State private var taskTitle: String = ""
var body: some View {
let binding = Binding<Date> {
formatter.date(from: taskData.taskDate ?? "") ?? Date.now
} set: { date, transaction in
taskData.taskDate = formatter.string(from: date)
}
VStack{
Form{
Section(header: Text("Name the task"),
footer: Text("Footer of a section can contain extra information about this section")) {
TextField("Name your task", text: $taskTitle)
.disableAutocorrection(true)
}
Section(header: Text("When do you need to do this"),
footer: Text("")) {
DatePicker(selection: binding, in: ...Date()){
Text("Pick time")
}
}
Button("Save and close"){
Task{
do{
try await managedObjContext.perform {
try managedObjContext.save()
}
dismiss.callAsFunction()
} catch{
print(error)
}
}
}
}.navigationBarBackButtonHidden()
}
}
}
\
Added an image of my new core data and hopefully the naming conventions are clearer
\
core data
core data
and
current errors
[Entering in product details error][3]
Product card errors

How to make the date change simultaneously in all views?

Date not change simultaneously in all views.
I want to link two calendars. Standard and custom. But they don't connect.
When I change the date in one, it doesn't change in the other.
I made Published:
import Combine
import Foundation
class CustomCalendar: ObservableObject {
#Published var currentDate = Date()
var currentThreeWeek: [Date] = []
init() {
fetchCurrentThreeWeek()
}
func fetchCurrentThreeWeek() {
let calendar = Calendar.current
var todayDay = DateInterval(start: Date(), duration: 1814400).start
let lastDay = DateInterval(start: Date(), duration: 1814400).end
currentThreeWeek.append(todayDay)
while todayDay < lastDay {
todayDay = calendar.date(byAdding: .day, value: 1, to: todayDay)!
currentThreeWeek.append(todayDay)
}
}
func extractDate(date: Date, format: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
dateFormatter.locale = Locale(identifier: "rus")
return dateFormatter.string(from: date)
}
func isToday(date: Date) -> Bool {
let calendar = Calendar.current
return calendar.isDate(currentDate, inSameDayAs: date)
}
}
When I select a date it doesn't change in other views.
import SwiftUI
struct FilterView: View {
#StateObject private var calendar = CustomCalendar()
#Binding var filterViewIsPresented: Bool
let todayDay = DateInterval(start: Date(), duration: 1814400).start
let lastDay = DateInterval(start: Date(), duration: 1814400).end
var body: some View {
VStack {
DatePicker("", selection: $calendar.currentDate, in: todayDay...lastDay, displayedComponents: .date)
.labelsHidden()
.environment(\.locale, Locale.init(identifier: "ru"))
HorizontalCalendarView()
HorizontalCalendarView()
}
}
}
struct FilterView_Previews: PreviewProvider {
static var previews: some View {
FilterView(filterViewIsPresented: .constant(false))
}
}
Custom calendar. On tap Gesture I change currentDate
import SwiftUI
struct HorizontalCalendarView: View {
#StateObject private var calendar = CustomCalendar()
var body: some View {
ScrollViewReader { value in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 0) {
ForEach(calendar.currentThreeWeek, id: \.self) { day in
VStack(spacing: 0) {
Text(calendar.extractDate(date: day, format: "dd"))
.font(.title3)
.fontWeight(.bold)
Text(calendar.extractDate(date: day, format: "EEE"))
RoundedRectangle(cornerRadius: 10, style: .continuous)
.frame(width: calendar.isToday(date: day) ? 40 : 0, height: 5)
.opacity(calendar.isToday(date: day) ? 1 : 0)
.padding(4)
}
.frame(width: 45, height: 45)
.foregroundStyle(calendar.isToday(date: day) ? .primary : .secondary )
.foregroundColor(calendar.isToday(date: day) ? .white : .black)
.padding(8)
.background(
ZStack {
if calendar.isToday(date: day) {
RoundedRectangle(cornerRadius: 10, style: .continuous)
}
}
)
.onTapGesture {
withAnimation(.easeIn(duration: 0.2)) {
calendar.currentDate = day
value.scrollTo(calendar.currentDate, anchor: .leading)
}
}
}
.padding(9)
}
}
Text(calendar.currentDate.formatted())
}
}
}
struct HorizontalCalendarView_Previews: PreviewProvider {
static var previews: some View {
HorizontalCalendarView()
}
}
How can I do this?
You have two options:
Pass calendar in HorizontalCalendarView constructor and use #ObservedObject property wrapper instead of #StateObject:
struct HorizontalCalendarView: View {
#ObservedObject private var calendar: CustomCalendar
init(_ calendar: CustomCalendar) {
self.calendar = calendar
}
...
and just pass it in FilterView
HorizontalCalendarView(calendar)
Another option is to use #EnvironmentObject (it's preferred method for deeply nested views in your example option 1 is better):
struct HorizontalCalendarView: View {
#EnvironmentObject private var calendar: CustomCalendar = CustomCalendar()
...
then you have to pass calendar with environmentObject modifier:
HorizontalCalendarView().environmentObject(calendar)
Note: in order to #EnvironmentObject works as expected it is not necessary to use environmentObject modifier on actual view, you can use this modifier in any of parent views. That makes it perfect for deep nested views

Overlay a color over SwiftUI view?

For my SwiftUI notes app, I would like to overlay a color over the entire view, but I've only been able to cover half off the screen with the color. Other attempts at setting the color and an overlay has only resulted in errors so far. Is there a solution to this? Or should go about this another way? Image has been provided as well. 1
import SwiftUI
let dateFormatter2 = DateFormatter()
struct NoteItem2: Codable, Hashable, Identifiable {
let id: Int
let text: String
var date = Date()
var dateText: String {
dateFormatter.dateFormat = "h:mm a"
return dateFormatter.string(from: date)
}
}
struct green_screen : View {
#State var items: [NoteItem] = {
guard let data = UserDefaults.standard.data(forKey: "notes") else { return [] }
if let json = try? JSONDecoder().decode([NoteItem].self, from: data) {
return json
}
return []
}()
#State var taskText: String = ""
#State var showAlert = false
#State var itemToDelete: NoteItem?
var alert: Alert {
Alert(title: Text("Hey!"),
message: Text("Are you sure you want to delete this item?"),
primaryButton: .destructive(Text("Delete"), action: deleteNote),
secondaryButton: .cancel())
}
var inputView: some View {
Color.green
.overlay(
HStack {
TextField("Write a note for yourself...", text: $taskText)
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
.clipped()
Button(action: didTapAddTask, label: { Text("Add") }).padding(8)
})
.edgesIgnoringSafeArea(.all)
}
var body: some View {
VStack {
inputView
Divider()
List(items) { item in
VStack(alignment: .leading) {
Text(item.dateText).font(.headline)
Text(item.text).lineLimit(nil).multilineTextAlignment(.leading)
}
.onLongPressGesture {
self.itemToDelete = item
self.showAlert = true
}
}
.alert(isPresented: $showAlert, content: {
alert
})
}
}
func didTapAddTask() {
let id = items.reduce(0) { max($0, $1.id) } + 1
items.insert(NoteItem(id: id, text: taskText), at: 0)
taskText = ""
save()
}
func deleteNote() {
guard let itemToDelete = itemToDelete else { return }
items = items.filter { $0 != itemToDelete }
save()
}
func save() {
guard let data = try? JSONEncoder().encode(items) else { return }
UserDefaults.standard.set(data, forKey: "notes")
}
}
struct green_screen_Previews: PreviewProvider {
static var previews: some View {
green_screen()
}
}
First you have to add green color to entire view instead of the inputView.
var inputView: some View {
GeometryReader{ geo in
HStack {
TextField("Write a note for yourself...", text: $taskText)
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
.clipped()
Button(action: didTapAddTask, label: { Text("Add") }).padding(8)
}
.frame( height: geo.size.height/2)
}
}
and main view
VStack {
inputView
Divider()
List(items) { item in
VStack(alignment: .leading) {
Text(item.dateText).font(.headline)
Text(item.text).lineLimit(nil).multilineTextAlignment(.leading)
}
.onLongPressGesture {
self.itemToDelete = item
self.showAlert = true
}
}
.listRowBackground(Color.green)
.alert(isPresented: $showAlert, content: {
alert
})
}
.background(Color.green) // <-- here
.ignoresSafeArea() // <-- here
after this modification also you cannot see the color overlay on entire screen because of SwiftUI list block the view color from it's default background color.so you need to change it clear color,
init() {
UITableView.appearance().separatorStyle = .none
UITableViewCell.appearance().backgroundColor = .clear
UITableView.appearance().backgroundColor = .clear
}

WatchOS ScrollReader Proxy Sluggish Scrolling

For WatchOS, I'm trying to build a ScrollView that auto scrolls to the last item added to a list. This all works fine, however once the ScrollView is full of content, the scrollTo func becomes very sluggish and slow. Setting the animation duration for withAnimation has no effect at this point. Any ideas why the animation begins to slow down? Thanks in advance.
struct ContentView: View {
#State private var items: [Date] = []
static var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm:ss:SSS"
return formatter
}()
var body: some View {
VStack {
ScrollView {
ScrollViewReader { scrollProxy in
LazyVStack {
ForEach(items, id: \.self) { item in
Text(Self.dateFormatter.string(from: item))
.font(.system(size: 16.0))
.padding()
.background(
RoundedRectangle(cornerRadius: 8.0)
.fill(Color.blue)
)
.id(item)
}
}
.onReceive(items.publisher) { date in
withAnimation(.easeInOut(duration: 0.25)) {
scrollProxy.scrollTo(date, anchor: .bottom)
}
}
}
}
Button("Add Item", action: { items.append(Date()) })
}
}
}

SwiftUI: Is there any solution to clear value in DatePicker?

I am finding a solution to clear value of a DatePicker in SwiftUI. I tried and it's not success. Please check:
struct EditProfileView: View {
#State var birthDate = Date()
var body: some View {
Form {
Section (header: Text("Birth day")) {
DatePicker(selection: $birthDate, in: ...Date(), displayedComponents: .date) {
Text("Select a date")
}
HStack {
Text("You were born in \(birthDate, formatter: dateFormatter)")
.font(.subheadline)
.foregroundColor(Color.gray)
Spacer()
Button(action: {
self.clearDate()
}) {
Text("Clear")
}
}
}
}
}
func clearDate () {
self.$birthDate = nil
}
}
This line of code is not work:
self.$birthDate = nil
I think because of Date type cant not leave nil but I cant find a solution to handle with it
Set the birthDate (not $birthDate) to the following, which will default back to the current date:
self.birthDate = Date()
You can create a Binding from Date?
struct EditProfileView: View {
#State var birthDate : Date?
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .short
return formatter
}
var body: some View {
Form {
Section (header: Text("Birth day")) {
DatePicker(selection: Binding<Date>(get: { () -> Date in
self.birthDate ?? Date()
}, set: { (Date) in
self.birthDate = Date
}), in: ...Date(), displayedComponents: .date) {
Text("Select a date")
}
HStack {
Text( "You were born in :") + (birthDate == nil ? Text("") : Text("\(birthDate!, formatter: dateFormatter)"))
.font(.subheadline)
.foregroundColor(Color.gray)
Spacer()
Button(action: {
self.clearDate()
}) {
Text("Clear")
}
}
}
}
}
func clearDate () {
self.birthDate = nil
}
}