SwiftUI: PrivacySensitive doesn't work for iOS16 - swiftui

I would like to redact a text on my widget when the device is locked. The below code works when I test it on iOS15, but didn't work on iOS16.
struct WidgetView : View {
var body: some View {
Text("Some sensitive text")
.privacySensitive()
}
}
Above applies to testing on both simulator and actual devices. I use xcode 14.2

Related

SwiftUI: present popover on iPhone?

I am looking at the Apple Reminders app and want to build the same pop over like view on iPhone. This the screen I am referring to:
So, I can present a popover like UI on an iPad using the popover modifier.
struct ContentView: View {
// 1.
#State var isPopoverPresented = false
var body: some View {
// 2.
Button(action: {
self.isPopoverPresented = true
}) {
Text("Some content")
}
// 3.
.popover(isPresented: $isPopoverPresented) {
Text("Popover is Presented")
.font(.largeTitle)
.frame(width: 200, height: 300)
}
}
}
However when that code runs on an iPhone, the popover turns in a fullscreen modal coming up from the bottom.
I want to know if there is a native way to build a screen like the one shown in the Reminders app or, is that screen a custom View with custom layout logic on an iPhone?
You're looking for a Menu (UIMenu in UIKit). Note that it's iOS 14+ only.

SwiftUI controls don't respond to input (clicks) in Catalyst if revealed in ScrollView

I'm dealing with what I am nearly certain to be a SwiftUI/Catalyst bug and am looking for a solution to get around it.
In the following code, about 30% of the time (5/15 in my tests), once the controls are revealed, the Toggle elements do not respond to clicks (and thus do not turn on/off).
I'm testing on Xcode 12.3 on Big Sur 11.1, running this code in Catalyst. It does work as expected 100% of the time as far as I can tell on iOS 14.3.
struct ContentView: View {
var body: some View {
ScrollView {
Row()
Row()
}
.padding()
}
}
struct Row : View {
#State private var showControls = false
#State private var toggleOn = false
var body: some View {
VStack {
HStack {
Text("Top section")
Button("\(showControls ? "Hide" : "Show") controls") {
showControls.toggle()
}
}
.frame(height: 100)
if showControls {
HStack {
Toggle("Toggle", isOn: $toggleOn)
Toggle("Toggle", isOn: $toggleOn)
}
}
}
}
}
The problem seems to come from having the Rows embedded in the ScrollView. The problem disappears completely if the controls start in their visible state (ie showControls = true), and only happens when they get revealed (ie showControls.toggle()) after the app starts.
I've also noticed that while Toggle and Slider fail about 30% of the time, a plain Button seems to be responsive 100% of the time.
The view debugger doesn't show anything 'in front' of the views that would be intercepting clicks.
I've tried changing to a List, which solves the problem, but yields other unfortunate side effects in behavior that I'd like to avoid in my real non-trivial app.
Can anyone else think of a reliable solution to avoiding this?

SwiftUI: how do I conditionally present ActionSheet only on iOS?

I’ve started to like an approach where I write crossplatform UI code with SwiftUI. The app would still be started with a native window/container, but have a fully crossplatform SwiftUI-driven UI. For many standard things things like list, navigationview etc, it is very useful and works fine.
The problem arises with some platform-specific view extensions. I would like to write this code in a platform-agnostic fashion, but not sure how to do it for some specific cases.
First, here’s a working example of a crossplatform conditional view modifier.
import SwiftUI
struct DemoView: View {
var body: some View {
Text("Hello, demo!").padding()
.modifier(iosBackground())
}
}
struct iosBackground: ViewModifier {
#if os(OSX)
func body(content: Content) -> some View {
content
}
#else
func body(content: Content) -> some View {
content.background(Color.blue)
}
#endif
}
What the iosBackground modifier is doing, is applying a view modification only on the iOS platform (well, to be specific, on any non-OSX platform, but let’s just work with OSX and iOS in this example). The OSX version of the view is passed through, while the iOS version returns a modified view. This color example is of course dumb and useless, but for layout-related things like padding, it is a highly practical approach.
My question: how do I apply the same approach to modifiers like actionSheet? Here’s what I would like to do:
struct DemoView: View {
#State var showActionSheet = true
var body: some View {
Text("Hello, demo!").padding()
.modifier(iosBackground())
.actionSheet(isPresented: $showActionSheet) {
ActionSheet(
title: Text("Actions"),
message: Text("Available actions"),
buttons: [
.cancel { },
.default(Text("Action")),
.destructive(Text("Delete"))
]
)
}
}
}
If you try to compile this code, it works fine on iOS. On OSX, it has a compilation error because the actionSheet API is not available on OSX. Which, indeed, is the case. I would like to make it so that the actionSheet call would simply be a no-op on OSX, but I can’t figure out how to structure and conditionally compile my code to make it happen.
The question, once again: how can I structure this code so that on iOS, actionSheet would be presented, while on OSX, it would be a no-op?
You are almost there. You would have found the way if you took a look at the .actionSheet's function signature. It returns an opaque type of some View that is the return type of almost all the SwiftUI views. So, look at the documentation too:
/// Presents an action sheet.
///
/// - Parameters:
/// - isPresented: A `Binding` to whether the action sheet should be
/// shown.
/// - content: A closure returning the `ActionSheet` to present.
#available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)
#available(OSX, unavailable)
public func actionSheet(isPresented: Binding<Bool>, content: () -> ActionSheet) -> some View
That being said, you could use this as like as you have used the .background function in conjunction with the content. So, here is the solution:
struct Sheet: ViewModifier {
#Binding var presented: Bool
func body(content: Content) -> some View {
#if os(OSX)
return content
#else
return content
.actionSheet(isPresented: $presented) {
ActionSheet(title: Text("Action Title"),
message: Text("Action Message"),
buttons: [.cancel(), .default(Text("Ok"))]
)
}
#endif
}
}
I just moved the #if - #endif inside the function body and that requires the return keyword explicitly. And you would use this as any modifier:
.modifier(Sheet(presented: $showActionSheet))

Create floating view with custom outline like in Notability

How to create such a view floating view with a custom boarder as shown in the picture? And such that it disappears as soon as the user clicks outside of the view.
Normally you would do that with a Popover like this:
#State var isPresented = false
var body: some View {
Button(action: {
self.isPresented = true
}) {
Text("Press me")
}.popover(isPresented: $isPresented, arrowEdge: .top) {
Text("Pop!") // You can put you own custom view here for the popover
}
}
Although it works as intended on the iPad (and I believe tvOS too, but I haven't tested it), it does not work properly with the current version of SwiftUI (as of 10/12/2019) on iPhones. Currently, the above code will just result in a somewhat glitchy modal on an iPhone, which I don't think is the intended function of it on iPhones. Apple's documentation for popover isn't very helpful right now, but here it is anyway.
For you information .popover is unabailable in tvOS.

SwiftUI achieve Master and Detail structure in tvOS

The way to achieve Master and Detail structure, as of beta 4, it should be by using .navigationViewStyle(.doubleColumn). Works perfectly on iOS / iPadOS / macOS but not in tvOS... it's a bug or I'm missing something?
import SwiftUI
struct ContentView: View {
var arra = ["Margherita","Marinara","Calzone"]
var body: some View {
NavigationView {
List(arra, id: \.self) { pizza in
NavigationLink(destination: SecondView(pizza: pizza)) {
Text(pizza)
}
}.navigationBarTitle("MASTER")
SecondView(pizza: arra[0])
}
.navigationViewStyle(.doubleColumn)
}
}
struct SecondView: View {
var pizza : String
var body: some View {
Text(pizza)
.navigationBarTitle("DETAIL")
}
}
(as of GM release) The official doc tells something like: double column style will be stacked on tvOS, like it's an iPhone in portrait mode. So it's impossible to automatically achieve the "master and detail" look like it's an iPad in landscape, you have to build it for yourself. Even if you use one UISplitViewController in UIKit! With this behavior I think it's good to go only if the master it's a fullscreen CollectionView, sorta like Netflix App.