SwiftUI and tvOS: adjustsImageWhenAncestorFocused - swiftui

How do you get the same focus effect in SwiftUI for images as you can with UIKit? I see you can use the card button style and it does provide motion effects but not the parallax that adjustsImageWhenAncestorFocused provides.
struct ContentView: View {
var body: some View {
Button {
print("tapped")
} label: {
AsyncImage(url: URL(string: "Image-URL"))
.frame(width: 300, height: 300)
}
.buttonStyle(.card)
}
}

I know this question is pretty old, but I ran into the same problem and found that wrapping your UIImageView in a UIViewRepresentable works just fine.

Related

Using a custom background behind NavigationStack in SwiftUI

This ought to be straightforward enough, but I cannot find out how to place a background behind a NavigationStack. With NavigationView, it was simply a matter of embedding in a ZStack with the background view called before the NavigationView (as described in an older post: How change background color if using NavigationView in SwiftUI?)
The same technique does not work for me with NavigationStack.
Here's what I have:
struct MyAngularGradient: View {
var body: some View {
ZStack {
AngularGradient(gradient: Gradient(colors: [.red, .orange , .yellow, .green, .cyan, .blue, .indigo, .purple, .red]), center: .leading)
AngularGradient(gradient: Gradient(colors: [.red, .orange , .yellow, .green, .cyan, .blue, .indigo, .purple, .red]), center: .leading)
.offset(x: -8)
}
.ignoresSafeArea()
}
}
var body: some View {
ZStack{
MyAngularGradient()
NavigationStack {
...
}
.navigationViewStyle(.stack)
} // end ZStack
FYI, I've used the same MyAngularGradient()in other apps (with NavigationView)
Any ideas? Thanks.
The closest I've managed to get so far is this:
struct MyView: View {
var body: some View {
NavigationStack {
ZStack {
Color.pink.ignoresSafeArea()
List {
NavigationLink("Hello") {
Text("Hello")
}
}
.navigationTitle("Title")
}
}
.scrollContentBackground(.hidden)
}
}
Which is somewhat dissatisfying for the following reasons:
When you scroll up the navigation bar appears, as expected, but ruins the effect imo.
I guess you can experiment changing this in a limited way using UINavigationBarAppearance() by updating it in the constructor of the view.
You can't apply a background to the whole app if you have multiple NavigationStackView based views in a TabView. (My example above was in a TabView)
When a new view is pushed on the stack the custom background disappears.
Only works on iOS 16+ due to the .scrollContentBackground(.hidden) modifier.
Does not work if you use ForEach to populate the List. Interestingly if you debug the hierarchy with and without the ForEach you'll see that SwiftUI adds a new view controller which is opaque in the case of ForEach.

GeometryReader acting weird when presenting a modal on iOS 16. Bug or new behavior?

I'm seeing a weird behavior that is affecting one of my views in SwiftUI after upgrading to iOS 16.
Just to give some context, here is the stack:
Xcode 14
Simulator or real device on iOS 15.5 and 16
Considering the minimum reproducible code below:
struct ContentView: View {
#State private var isPresented: Bool = false
var body: some View {
GeometryReader { reader in
VStack(spacing: 36) {
Text("Screen frame:\n\(String(describing: reader.frame(in: .global)))")
.multilineTextAlignment(.center)
Button {
isPresented.toggle()
} label: {
Text("Open modal")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
.onReceive(NotificationCenter.default.appDidBecomeActive()) { _ in
print(reader.frame(in: .global))
}
.onReceive(NotificationCenter.default.appDidEnterBackground()) { _ in
print(reader.frame(in: .global))
}
}
.sheet(isPresented: $isPresented) {
modalView
}
}
private var modalView: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.padding()
}
}
extension NotificationCenter {
func appDidBecomeActive() -> AnyPublisher<Notification, Never> {
publisher(for: UIApplication.didBecomeActiveNotification).eraseToAnyPublisher()
}
func appDidEnterBackground() -> AnyPublisher<Notification, Never> {
publisher(for: UIApplication.didEnterBackgroundNotification).eraseToAnyPublisher()
}
}
As soon the view starts, it's possible to see the frame available due to the GeometryReader. Then following the steps:
Open the modal view
Send the app to the background
Open the app again
Close the modal
It's possible to see that the frame changed, and the values match with the 3D effect when a view is presenting another view, and it's never changing again to the right values unless you send the app again to the background or switch views (e.g. using a TabView).
I don't find anything on iOS release notes talking about it, so I supposed it must be a bug (I've filled out a bug report already).
On iOS 15, the frame value keeps stable at the same value.
I have a couple of views relying on the value of a GeometryReader, and it's causing my view to deform because of this issue. Does anyone know a way to force the recalculation for the GeometryReader for this case?
Any help is appreciated.
The issue won't occur if you control the display of the sheet with the new presentationDetents method, provided you do not request to cover the entire screen.
I modified your code as follows:
.sheet(isPresented: $isPresented) {
if #available(iOS 16, *) {
modalView
.presentationDetents([.fraction(0.99)])
}
else {
modalView
}
}
The issue will remain if you request .fraction(1), i.e. covering the whole screen.

Does Xcode SwiftUI button default layout has border?

I am using Xcode to create an app that requires buttons. Right now, when I create a button, I get the text label, which I want, but I also get a background with rounded corners around it. I want to have the button with just the label but without the background. I was using Swift Playgrounds before Xcode and did not have this problem.
Here is my code:
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Rectangle()
.frame(width: 1000, height: 500)
.foregroundColor(.red)
Button(action: {
}) {
Text("Button")
}
}
}
}
Customize the button's style with the .buttonStyle view modifier:
Button(action: {}) {
Text("Button")
}
.buttonStyle(.borderless)
.borderless, .plain, and .link are the options that will result in no border.
Here is a Button without any background or border.
Button("Click") {
//do something
}
.background(.clear) //this

SwiftUI Is there any built in view that kind of slides in from the side and takes up 3/4 of the screen?

This is probably a custom view but in the Reddit app there's a toolbar and the top left button(3 lines) opens this kind of view from the side that moves the current view to the right so you can only see about 25% of it and a new view that takes up about 75% of the screen slides in. Is there anything like this built into SwiftUI and if there isn't how would I go about implementing something like this?
This is my custom side bar behave similarly to what you just mentioned, you can try it. (Images and Code are below)
Before click:
After clicked:
struct ContentView: View {
#State var isClicked = false
var body: some View {
HStack {
Rectangle()
.fill(.orange)
.frame(width: isClicked ? UIScreen.main.bounds.width * 0.75 : 0)
VStack {
HStack {
Button {
withAnimation {
isClicked.toggle()
}
} label: {
Image(systemName: "menucard.fill")
.padding(.leading)
}
Spacer()
}
Spacer()
}
}
}
}

UIViewRepresentable view disappears when contextMenu is activated

I'm trying use context menu with UIViewRepresentable.
When context menu is activated, UIViewRepresentable disappears.
Here is the code:
UIViewRepresentable view:
struct TestView: UIViewRepresentable {
func makeUIView(context: Context) -> some UIView {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 150, height: 150))
view.backgroundColor = UIColor.red
return view
}
func updateUIView(_ uiView: UIViewType, context: Context) {
//
}
}
ContentView:
struct ContentView: View {
var body: some View {
TestView()
.frame(width: 200, height: 200)
.contextMenu {
Text("Context Menu")
}
}
}
How to make UIViewRepresentable not disappear?
I ran into this exact issue recently with my UIViewRepresentable view that returns a custom UIImageView. The fix was adding a clipped() modifier to the view with the context menu. Not sure if this applies to your case though as mine involved an image.
This was tested on iPhone 12 Pro running iOS 15.1.
I ran into this same issue and found that the clipped() modifier works fine on iOS 15.0 and later. But it doesn't fix the problem on iOS 14.
As I was experimenting with different approaches I discovered that the compositingGroup() fixes the problem on iOS 14.0 and later including iOS 15.
struct ContentView: View {
var body: some View {
TestView()
.frame(width: 200, height: 200)
.compositingGroup()
.contextMenu {
Text("Context Menu")
}
}
}
UIViewRepresentable rendered correctly within a contextMenu