Does `ShapeStyle` inherit from `View`? - swiftui

In the official document about ShapeStyle, there is nowhere to find out whether ShapeStyle inherits from something else or not, is it a View?

Here is the definition of ShapeStyle. As you can see, it conforms to View protocol.
/// A way to turn a shape into a view.
#available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol ShapeStyle {
}
#available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension ShapeStyle {
/// Return a new paint value matching `self` except using `rect` to
/// map unit-space coordinates to absolute coordinates.
#inlinable public func `in`(_ rect: CGRect) -> some ShapeStyle
}
/// Default View.body implementation to fill a Rectangle with `self`.
#available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension ShapeStyle where Self : View, Self.Body == _ShapeView<Rectangle, Self> {
public var body: _ShapeView<Rectangle, Self> { get }
}

Related

Is there a way to set font tracking (i.e. spacing) inside a custom SwiftUI ButtonStyle?

In SwiftUI you can set font tracking (spacing between letters) on a Text View using the tracking View modifier:
Text("Hello, World!")
.tracking(10)
If you have a Button and apply your custom ButtonStyle you can do all kinds of modifications to the style of the button, but since the configuration.label is not an instance of Text but rather ButtonStyleConfiguration.Label, I don't have direct access to apply tracking. Is there a way to do this without having to set it on the Button's label View directly? It seems like something you ought to be able to do since it's a style-related thing but I don't see how to accomplish it.
public struct MyStyle: ButtonStyle {
public func makeBody(configuration: Configuration) -> some View {
configuration.label
// (if I tried to put `.tracking` here it wouldn't work because it isn't Text)
.foregroundColor(Color.red)
.clipShape(Capsule())
// … etc.
}
}
ViewModifier to change both font and tracking looks ~ related but no relevant answer.
Yes, ButtonStyleConfiguration.Label is not matched to Text, but it is your style you can ignore standard label, and request own by interface contract, exactly which you need (Text in this case), like in below demo
public struct MyStyle: ButtonStyle {
let label: Text // << here !!
public func makeBody(configuration: Configuration) -> some View {
label // << here !!
.tracking(10)
.foregroundColor(configuration.isPressed ? .black : .red)
.clipShape(Capsule())
}
}
and use it as (or create own button extension with button builder to hide those unneeded curls)
Button(action: {
// action here
}){}
.buttonStyle(MyStyle(label: Text("Hello")))
Tested with Xcode 13.2 / iOS 15.2

Swiftui 3 API cleanup: is it achievable with custom styles?

Swiftui 3 and iOS15 gave us the possibility to use styling more easily, so instead of
.pickerStyle(WheelPickerStyle())
we might use
.pickerStyle(.wheel)
At the same time, we can determine custom styles, for example:
struct CustomButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.foregroundColor(Color.blue)
.background(RoundedRectangle(cornerRadius: 12).fill(Color.red))
.scaleEffect(configuration.isPressed ? 0.9 : 1.0)
.frame(width: 40, height: 40)
}
}
However, to use the above custom style, I have to write
.buttonStyle(CustomButtonStyle())
How might I achieve the possibility to use the custom style with a simple modifier like this?
.buttonStyle(.custom)
We can create extension to ButtonStyle, like
extension ButtonStyle where Self == CustomButtonStyle {
static var custom: CustomButtonStyle { CustomButtonStyle() }
}
and then use it as
.buttonStyle(.custom)
Tested with Xcode 13 / iOS15
Note: this approach does not work with Xcode 12

SwiftUI iOS14 - Disable keyboard avoidance

Is there a way to disable the native keyboard avoidance on iOS14?
There is no keyboard avoidance in iOS13, so I want to implement my own, but when I do the native one on iOS14 is still active, so both my implementation and the native one run at the same time, which I don't want.
My deployment target is iOS13, so I need a solution for both iOS13 and iOS14.
You can use if #available(iOS 14.0, *) if you want to adapt the iOS 14 code so it compiles on iOS 13.
Here is an adapted version of this answer to work on both iOS 13 and iOS 14:
struct ContentView: View {
#State var text: String = ""
var body: some View {
if #available(iOS 14.0, *) {
VStack {
content
}
.ignoresSafeArea(.keyboard, edges: .bottom)
} else {
VStack {
content
}
}
}
#ViewBuilder
var content: some View {
Spacer()
TextField("asd", text: self.$text)
.textFieldStyle(RoundedBorderTextFieldStyle())
Spacer()
}
}

issue with if #available SwiftUI less than iOS 14

I'm using the beta version of Xcode 12.0 to play around with LazyVGrid, to render this grid within a scrollview if the phone has iOS 14, otherwise just to render the ScrollView as one column.
When I launch this on the app on my phone (not using iOS 14), opening this view causes my app to crash. But if I comment out the "if #available" section and just display what's in the "else" statement, it works fine.
Is there an issue with if #available in earlier versions of iOS or is my syntax just incorrect?
var body: some View {
NavigationView {
VStack {
//Empty View navigation link to choose the selected pack in User Defaults.
ScrollView(.vertical, showsIndicators: false) {
//Checks if iOS version 14.0 is available to render the lazy grid view
if #available(iOS 14.0, *) {
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 15) {
//checks if the pack is in the purchased list - if so, renders it as an unlocked tile.
ForEach((allPacks), id: \.self) { pack in
UnlockedPackTile(tilePack: pack)
.onTapGesture {
print("Originally tapped \(pack.name)")
self.userInformation.defaultPack = pack
self.isPresented.toggle()
}
}
}
} else {
//does this as a simple stack instead if iOS 14 is not available.
ForEach((allPacks), id: \.self) { pack in
UnlockedPackTile(tilePack: pack)
.onTapGesture {
print("Originally tapped \(pack.name)")
self.userInformation.defaultPack = pack
self.isPresented.toggle()
}
}
}
}
Try to wrap content of ScrollView into Group (or VStack) like
ScrollView(.vertical, showsIndicators: false) {
Group {
if #available(iOS 14.0, *) {
// ... new content here
} else {
// ... old content here
}
}
}

Why updating #State inside AppDelegate does not work?

import Cocoa
import SwiftUI
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#State var isFocused: Bool = true
var window: NSWindow!
// Some window setup code here
func applicationDidBecomeActive(_ notification: Notification) {
isFocused = true
}
func applicationDidResignActive(_ notification: Notification) {
isFocused = false
}
}
I tried to print isFocused value and it is always true even immediately after isFocused = false statement. Is #State only working inside SwiftUI Views?
Documentation:
/// A linked View property that instantiates a persistent state
/// value of type `Value`, allowing the view to read and update its
/// value.
#available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
#frozen #propertyWrapper public struct State<Value> : DynamicProperty {
So, State is for SwiftUI View, but AppDelegate is-not-a View.