在.onchange中添加视图修饰符
是否可以在内部添加视图修饰符.onchange
?
简化的示例:
content
.onChange(of: publishedValue) {
content.foregroundColor(.red)
}
我有一个主题,当更改需要更改状态栏颜色时。我有一个为此创建的视图修饰符( https://barstool.engineering/set-the--ios-status-bar-style-in-swiftui-in-swiftui-is-a-custom-view-modifier/ )。修饰符正常工作,但是我需要将其更新为已发布的Value
更改。
实际最小示例:
import SwiftUI
struct ContentView: View {
@ObservedObject var viewModel: TestViewModel
var body: some View {
ZStack {
Rectangle().foregroundColor(.mint)
VStack(alignment: .center, spacing: 25) {
Text("Test text \(viewModel.publishedValue)")
.onChange(of: viewModel.publishedValue) { newValue in
// Change status bar color
if viewModel.publishedValue % 2 == 0 {
self.body.statusBarStyle(.lightContent)
} else {
self.body.statusBarStyle(.darkContent)
}
}
Button("Increment") {
viewModel.publishedValue += 1
}
}
}
.ignoresSafeArea()
.statusBarStyle(.lightContent)
}
}
class TestViewModel: ObservableObject {
@Published var publishedValue: Int
init(publishedValue: Int) {
self.publishedValue = publishedValue
}
}
extension View {
/// Overrides the default status bar style with the given `UIStatusBarStyle`.
///
/// - Parameters:
/// - style: The `UIStatusBarStyle` to be used.
func statusBarStyle(_ style: UIStatusBarStyle) -> some View {
return self.background(HostingWindowFinder(callback: { window in
guard let rootViewController = window?.rootViewController else { return }
let hostingController = HostingViewController(rootViewController: rootViewController, style: style)
window?.rootViewController = hostingController
}))
}
}
fileprivate class HostingViewController: UIViewController {
private var rootViewController: UIViewController?
private var style: UIStatusBarStyle = .default
init(rootViewController: UIViewController, style: UIStatusBarStyle) {
self.rootViewController = rootViewController
self.style = style
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func viewDidLoad() {
super.viewDidLoad()
guard let child = rootViewController else { return }
addChild(child)
view.addSubview(child.view)
child.didMove(toParent: self)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return style
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
setNeedsStatusBarAppearanceUpdate()
}
}
fileprivate struct HostingWindowFinder: UIViewRepresentable {
var callback: (UIWindow?) -> ()
func makeUIView(context: Context) -> UIView {
let view = UIView()
DispatchQueue.main.async { [weak view] in
self.callback(view?.window)
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
// ...
}
}
示例项目的github repo: https://github.com/iikeli/iikeli/iikeli/ikeli/view -Modifier-Test
Is it possible to add a view modifier inside .onChange
?
Simplified example:
content
.onChange(of: publishedValue) {
content.foregroundColor(.red)
}
I have a theme that when changed needs to change the status bar color. I have a view modifier created for that ( https://barstool.engineering/set-the-ios-status-bar-style-in-swiftui-using-a-custom-view-modifier/ ). The modifier works fine, but I need to update it as the publishedValue
changes.
Actual minimal example:
import SwiftUI
struct ContentView: View {
@ObservedObject var viewModel: TestViewModel
var body: some View {
ZStack {
Rectangle().foregroundColor(.mint)
VStack(alignment: .center, spacing: 25) {
Text("Test text \(viewModel.publishedValue)")
.onChange(of: viewModel.publishedValue) { newValue in
// Change status bar color
if viewModel.publishedValue % 2 == 0 {
self.body.statusBarStyle(.lightContent)
} else {
self.body.statusBarStyle(.darkContent)
}
}
Button("Increment") {
viewModel.publishedValue += 1
}
}
}
.ignoresSafeArea()
.statusBarStyle(.lightContent)
}
}
class TestViewModel: ObservableObject {
@Published var publishedValue: Int
init(publishedValue: Int) {
self.publishedValue = publishedValue
}
}
extension View {
/// Overrides the default status bar style with the given `UIStatusBarStyle`.
///
/// - Parameters:
/// - style: The `UIStatusBarStyle` to be used.
func statusBarStyle(_ style: UIStatusBarStyle) -> some View {
return self.background(HostingWindowFinder(callback: { window in
guard let rootViewController = window?.rootViewController else { return }
let hostingController = HostingViewController(rootViewController: rootViewController, style: style)
window?.rootViewController = hostingController
}))
}
}
fileprivate class HostingViewController: UIViewController {
private var rootViewController: UIViewController?
private var style: UIStatusBarStyle = .default
init(rootViewController: UIViewController, style: UIStatusBarStyle) {
self.rootViewController = rootViewController
self.style = style
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func viewDidLoad() {
super.viewDidLoad()
guard let child = rootViewController else { return }
addChild(child)
view.addSubview(child.view)
child.didMove(toParent: self)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return style
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
setNeedsStatusBarAppearanceUpdate()
}
}
fileprivate struct HostingWindowFinder: UIViewRepresentable {
var callback: (UIWindow?) -> ()
func makeUIView(context: Context) -> UIView {
let view = UIView()
DispatchQueue.main.async { [weak view] in
self.callback(view?.window)
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
// ...
}
}
GitHub repo for the example project: https://github.com/Iikeli/view-modifier-test
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
简短的答案是否定的,但这并不意味着您不能使用它来根据某些
.onchange(..)
操作更改视图。例如。您的用法可能看起来像这样。
The short answer is no, but that doesn't mean you can't use it to have views change based on some
.onChange(..)
action. For example.Your usage might look something like this.
首先,感谢您的帮助。在我的情况下,这两个答案都没有帮助,因为我无法通过可变更改进行更新。但是,通过一些谷歌搜索并尝试了不同的解决方案,我发现了一种工作解决方案,以更新状态栏颜色。
我需要在
hostingViewController
中更新样式变量,然后相应地进行更新。因此,我将hostingViewController
添加为@stateObject
,并在.onchange()
中更新了样式变量。我要开始的解决方案并不是一开始,但确实有效。代码:
这个答案给我所有实现解决方案所需的一切。注意:您可以覆盖
rootviewController
,但是您喜欢,我使用无论如何我们已经在项目中使用了它。github branch
First of all, thanks for the help. Neither of the answers helped in my situation, since I couldn't get the modifier to update with the variable change. But with some Googling and trying out different solutions I figured out a working solution for updating the status bar colors.
I needed to update the style variable in the
HostingViewController
, and then update accordingly. So I added theHostingViewController
as a@StateObject
and updated the style variable inside the.onChange()
. Not quite the solution I was going with to start out, but it does work.The code:
Big shoutout to this answer for giving me all that I needed to implement the solution. Note: You can override the
rootViewController
however you like, I used SwiftUI-Introspect as we are already using it in our project anyway.GitHub branch
您正在解决这个问题。由于您的
viewModel
是@observedObject
,而已发布Value
IS已发布
,您的view> view
代码>每次更新都会自动重新计算。无需手动onChange
。您只需将逻辑移动到
statusbarstyle
的输入参数中即可。甚至更好,将逻辑移至单独的计算属性:
You are way overcomplicating this. Since your
viewModel
is@ObservedObject
and thepublishedValue
isPublished
, the body of yourView
will be recalculated automatically every timepublishedValue
is updated. There's no need for a manualonChange
.You can simply move the logic into the input argument of
statusBarStyle
.Or even better, move the logic into a separate computed property: