SwiftUI - Change default Picker item height - ios

I am currently using SwiftUI's Picker API, and would like to increase the Picker row's height. The reason for this is when text has a bigger font, the items overlap.
I am trying to avoid using UIPickerView + UIViewRepresentable if possible & would like to achieve this using purely SwiftUI if possible.
I have tried using Spacers, padding modifiers and frame but no luck as of yet.
I know this feature is already available on watchOS, but are there any alternatives when it comes to iOS? https://developer.apple.com/documentation/swiftui/link/defaultwheelpickeritemheight(_:)
I have attached basic picker code with the aforementioned issue. Thanks in advance.
struct ContentView: View {
#State private var selectedNum = 0
var body: some View {
Picker("", selection: self.$selectedNum) {
Text("1").font(.system(size: 80))
Text("2").font(.system(size: 80))
Text("3").font(.system(size: 80))
Text("4").font(.system(size: 80))
Text("5").font(.system(size: 80))
Text("6").font(.system(size: 80))
}
.pickerStyle(.wheel)
}
}
This is the result of the code

Related

SwiftUI animation problem with a binding to a StateObject inside a NavigationView

I have an interesting situation in regards to animations in SwiftUI. In its simplified form, I have a view that shows a rectangle which, when tapped, should toggle a Bool binding and represent the change with an animated transition of color. But it seems like the animation doesn't happen when the view is inside a NavigationView and the binding is coming from a StateObject instead of simple local state. I can't explain why that would be the case, and would appreciate any thoughts.
Below is the code that shows a simplified case that reproduces the issue. The app code isn't particularly interesting; it's the default code that creates a WindowGroup and shows an instance of ContentView in it.
import SwiftUI
class AppState: ObservableObject {
#Published var isRed = false
}
struct RectView: View {
#Binding var isRed: Bool
var body: some View {
Rectangle()
.fill(isRed ? Color.red : Color.gray)
.frame(width: 75, height: 75, alignment: .center)
.onTapGesture {
withAnimation(.easeInOut(duration: 1)) {
isRed.toggle()
}
}
}
}
struct ContentView: View {
#StateObject var appState = AppState()
#State private var childViewIsRed = false
var body: some View {
VStack {
NavigationView {
List {
NavigationLink("Link with binding to state object", destination: RectView(isRed: $appState.isRed))
NavigationLink("Link with binding to state variable", destination: RectView(isRed: $childViewIsRed))
}
}
.frame(height: 300)
RectView(isRed: $appState.isRed)
RectView(isRed: $childViewIsRed)
}
}
}
The gif/video below is me demonstrating four things, tapping on these views from bottom to top:
First I tap on the very bottom rectangle - the one with a binding to a #State property. It toggles with animation as expected. I tap again to leave it gray.
Then I tap the second rectangle from the bottom - one with a binding to a #Published property in the #StateObject. All is well.
Next, I tap on the NavigationLink that leads to a rectangle that is bound to the local #State property. When I tap on the rectangle the transition is animated fine. The very bottom rectangle also animates, which makes sense since they are bound to the same property.
Finally I tap on the top NavigationLink, which leads to a rectangle bound to the #Published property in the #StateObject. When I tap on this rectangle though, there is no animation. The rectangle snaps to red. The rectangle below it (which is bound to the same property) animates fine, proving that the property is indeed toggled. But there is no animation inside the NavigationView. Why? What am I missing?
I've searched for existing questions. I'm aware that there are some around NavigationView (like this) but that doesn't explain why one type of binding would work fine inside a NavigationView and another wouldn't. Similarly, there are ones around animating changes to #ObservedObjects (like this, would a #StateObject be similar?) but I don't follow why animating changes to such a binding would work fine outside of a NavigatonView and not inside one.
In case it's relevant I'm using Xcode 13.2.1, running on macOS 12.2.1, which is in turn running on a 16-inch M1 Max MacBook Pro. The simulator shown in the gif/video is an iPhone 11 Pro Max. I get the same results if I deploy this tiny test app to a physical iPhone 7 running iOS 15.3.1.
To be honest with you, I am not sure why, but utilizing the animation modifier on the RectView allows the animation to occur without any issues.
struct RectView: View {
#Binding var isRed: Bool
var body: some View {
Rectangle()
.fill(isRed ? Color.red : Color.gray)
.frame(width: 75, height: 75, alignment: .center)
.animation(.easeOut(duration: 1), value: isRed)
.onTapGesture { isRed.toggle() }
}
}
screen recording example

difference between background color for TextField in SwiftUI between macOS and iOS?

I run into an issue I have no idea how to solve. I plan to have a shared code base for macOS and iOS with SwiftUI (as much as possible).
But already with something simple like setting background color for a text field there is a difference
Following code snippet:
struct NewTextField : View
{
#State var d:String = ""
var body: some View
{
HStack(content:
{
TextField("Date: ", text:$d)
.foregroundColor(.black)
.background(Color.green)
/// ... more views
}
}
}
This is simplified; I have multiple textfields on the real application with background colors depending on the content.
on iOS it looks ok; the full textfield background is set to the desired color; on macOS the TextField appear with white background and colored border.
What I'm doing wrong ?
just to leave it not unanswered:
added
.textFieldStyle(PlainTextFieldStyle())
and it create the desired effect
TextField("Date: ", text:$d)
.textFieldStyle(PlainTextFieldStyle())
.foregroundColor(.black)
.background(Color.green)
based on this post: SwiftUI customized text field in OSX

SwiftUI | customize dragitem appearance within LazyGrid using onDrag [duplicate]

I've been wondering if there is any way to customize the preview image of the view that's being dragged when using onDrag?
As you might see, the preview image is by default a slightly bigger opacity image of the view.
From what I have found, a preview image is generated at the very beginning of the dragging process. But I couldn't find a way to change it.
What I mean by customizing is to have some custom image or a preview image of a custom view. (Both without the default opacity)
Does anyone have an idea?
I have tried to use previewImageHandler and in general, read a lot about the NSItemProvider. But for me, it seems like this is something that is not possible for SwiftUI yet?
With UIKit one could have just customized the UIDragItem - something like that using previewProvider: Here
Here is my demo code:
struct ContentView: View {
var body: some View {
DraggedView()
.onDrag({ NSItemProvider() })
}
private struct DraggedView: View {
var body: some View {
RoundedRectangle(cornerRadius: 20)
.frame(width: 120, height: 160)
.foregroundColor(.green)
}
}
}
I will use this for drag and drop within a LazyVGrid, so custom gestures are unfortunately no option.
One second idea I had would be to have a gesture simultaneously that first changes the item to be dragged to something else and then onDrag starts and returns the NSItemProvider with the preview image which would be then the one I would want. But I couldn't have those two gestures go at the same time, you would have to dismiss one first in order to start the second.
Thank you!
iOS 15 adds an API to do this - you can specify the View to use for the preview. onDrag(_:preview:)
RoundedRectangle(cornerRadius: 20)
.frame(width: 120, height: 160)
.foregroundColor(.green)
.onDrag {
NSItemProvider()
} preview: {
RoundedRectangle(cornerRadius: 18)
.frame(width: 100, height: 140)
.foregroundColor(.green)
}

Runtime crash trying to modify SwiftUI Navigation Bar Text attributes

I am trying to modify the navigation bar text attributes in SwiftUI (to add a text shadow) and have hit a wall trying to understand why I'm receiving run-time crashes in the simulator. There was another thread (ref: below) that was able to solve changing the font type using init() to modify the appearance, however trying to use init to modify the Text("") method to add a shadow results in a crash.
I've also tried extracting the Text("NavBarTitle") into its own method, then applying modifiers (no luck there). As seen in my code I've tried extracting the text into a variable results in a crash. Even just applying the modifiers directly causes a crash.
I'm not experienced enough in SwiftUI to call this a bug but it really feels like one.
Thanks for your help in advance!
import SwiftUI
struct ContentView: View {
init() {
UINavigationBar.appearance().largeTitleTextAttributes = [.shadow: 5]
}
let navigationBarText: Text = Text("Navigation Bar")
var body: some View {
NavigationView {
VStack {
Text("Hello, World!")
}
.navigationBarTitle(navigationBarText)
}
}
}
ref: https://stackoverflow.com/a/57632229/1514009
It should be provided NSShadow object instead of number, as below
init() {
let shadow = NSShadow()
shadow.shadowOffset = CGSize(width: 5, height: 2)
UINavigationBar.appearance().largeTitleTextAttributes = [.shadow: shadow]
}

Modifiers of elements inside segmented picker don't work

I have a code:
#Binding var hand: Hand
var body: some View {
HStack {
Text("Hand: ")
Picker(selection: $hand, label: Text("Strength")) {
Text("JIUJIU").tag(0).foregroundColor(.blue)
Text("BLABLA").tag(1).rotationEffect(Angle(degrees: 35))
}.pickerStyle(SegmentedPickerStyle())
}
}
There is foregroundColor(.blue) and rotationEffect(Angle(degrees: 35)) modifiers inside elements of Picker. But in fact I see nor any effect:
Why modifiers don't work?
It is your pickerStyle(). The documentation says concerning the SegmentedPickerStyle:
Note: Only supports segments of type Label and Image. Passing any
other type of view will result in a visible, but empty, segment.
I think that is why you only see the text. I have tried using another style and that worked fine.

Resources