切换swiftUI toggle()时,如何触发操作?
在我的SwiftUI视图中,当Toggle()更改其状态时,我必须触发一个操作。切换本身只接受一个绑定。 因此,我尝试在@State变量的didSet中触发该操作。但是迪塞特从来没有被叫来过 是否有任何(其他)方式触发动作?或者用什么方法来观察@State变量的值变化 我的代码如下所示:切换swiftUI toggle()时,如何触发操作?,swift,toggle,swiftui,Swift,Toggle,Swiftui,在我的SwiftUI视图中,当Toggle()更改其状态时,我必须触发一个操作。切换本身只接受一个绑定。 因此,我尝试在@State变量的didSet中触发该操作。但是迪塞特从来没有被叫来过 是否有任何(其他)方式触发动作?或者用什么方法来观察@State变量的值变化 我的代码如下所示: func toggleAction(state: String, index: Int) -> String { print("The switch no. \(index) is \(state
func toggleAction(state: String, index: Int) -> String {
print("The switch no. \(index) is \(state)")
return ""
}
struct PWSDetailView : View {
@State var isDisplayed: Bool = false
@ObservedObject var station: PWS
...
var body: some View {
...
UIToggle(isOn: $isDisplayed) { isOn in
//Do something here with the bool if you want
//or use "_ in" instead, e.g.
if isOn != station.isDisplayed {
PWSStore.shared.toggleIsDisplayed(station)
}
}
...
}
}
struct PWSDetailView:View{
@ObjectBinding var站:PWS
@显示状态变量:Bool=false{
迪塞特{
如果显示!=station.isDisplayed{
PWSStore.shared.toggleIsDisplayed(站点)
}
}
}
var body:一些观点{
VStack{
ZStack(对齐:。引导){
矩形()
.框架(宽度:UIScreen.main.bounds.width,高度:50)
.foregroundColor(颜色为lokalZeroBlue)
文本(station.displayName)
.font(.title)
.foregroundColor(颜色.白色)
.padding(.leading)
}
地图视图(纬度:station.latitude,经度:station.latitude,跨度:0.05)
.框架(高度:UIScreen.main.bounds.height/3)
.padding(.top,-8)
形式{
切换(显示:$isOn)
{Text(“Wetterstation”)}
}
垫片()
}.colorScheme(.dark)
}
}
所需的行为是,当Toggle()更改其状态时,会触发操作“PWSStore.shared.toggleIsDisplayed(station)”。首先,您是否知道station.isDisplayed的额外KVO通知是一个问题?您是否遇到性能问题?如果没有,那就别担心了 如果您遇到性能问题,并且已经确定这些问题是由于过度显示的
station.isDisplayed
KVO通知造成的,那么下一步要尝试的是消除不必要的KVO通知。您可以通过切换到手动KVO通知来实现这一点
将此方法添加到站点的类定义中:
@objc class var automaticallyNotifiesObserversOfIsDisplayed: Bool { return false }
并使用Swift的willSet
和didSet
观察员手动通知KVO观察员,但仅当值发生变化时:
@objc dynamic var isDisplayed = false {
willSet {
if isDisplayed != newValue { willChangeValue(for: \.isDisplayed) }
}
didSet {
if isDisplayed != oldValue { didChangeValue(for: \.isDisplayed) }
}
}
您可以尝试以下方法(这是一种解决方法):
在它下面,创建如下函数:
func toggleAction(state: String, index: Int) -> String {
print("The switch no. \(index) is \(state)")
return ""
}
struct PWSDetailView : View {
@State var isDisplayed: Bool = false
@ObservedObject var station: PWS
...
var body: some View {
...
UIToggle(isOn: $isDisplayed) { isOn in
//Do something here with the bool if you want
//or use "_ in" instead, e.g.
if isOn != station.isDisplayed {
PWSStore.shared.toggleIsDisplayed(station)
}
}
...
}
}
我想没关系
struct ToggleModel {
var isWifiOpen: Bool = true {
willSet {
print("wifi status will change")
}
}
}
struct ToggleDemo: View {
@State var model = ToggleModel()
var body: some View {
Toggle(isOn: $model.isWifiOpen) {
HStack {
Image(systemName: "wifi")
Text("wifi")
}
}.accentColor(.pink)
.padding()
}
}
类PWSStore:observeObject{
...
var站:PWS
@已发布变量isDisplayed=true{
意志{
PWSStore.shared.toggleIsDisplayed(self.station)
}
}
}
结构PWSDetailView:视图{
@ObservedObject var station=PWSStore.shared
...
var body:一些观点{
...
切换(isOn:$isDisplayed){Text(“Wetterstation Anzegin”)}
...
}
}
在这里演示我找到了一个更简单的解决方案,只需使用ontapsignature:D
Toggle(isOn: $stateChange) {
Text("...")
}
.onTapGesture {
// Any actions here.
}
这是我的方法。我也面临同样的问题,但我决定将UIKit的UISwitch封装到一个符合UIViewRepresentable的新类中
导入快捷界面
最终类UIToggle:UIViewRepresentable{
@绑定变量:Bool
var changedAction:(Bool)->Void
init(isOn:Binding,changedAction:@escaping(Bool)->Void){
自已
self.changedAction=changedAction
}
func makeUIView(上下文:context)->UISwitch{
设uiSwitch=uiSwitch()
返回开关
}
func updateUIView(uiView:UISwitch,context:context){
uiView.isOn=isOn
uiView.addTarget(self,action:#选择器(switchHasChanged(:)),for:.valueChanged)
}
@objc func开关已更改(\发送方:UISwitch){
self.isOn=sender.isOn
changedAction(sender.isOn)
}
}
然后它的用法是这样的:
func toggleAction(state: String, index: Int) -> String {
print("The switch no. \(index) is \(state)")
return ""
}
struct PWSDetailView : View {
@State var isDisplayed: Bool = false
@ObservedObject var station: PWS
...
var body: some View {
...
UIToggle(isOn: $isDisplayed) { isOn in
//Do something here with the bool if you want
//or use "_ in" instead, e.g.
if isOn != station.isDisplayed {
PWSStore.shared.toggleIsDisplayed(station)
}
}
...
}
}
基于@Legolas Wang的回答 当您从切换隐藏原始标签时,您只能将Tap手势附加到切换本身
HStack {
Text("...")
Spacer()
Toggle("", isOn: $stateChange)
.labelsHidden()
.onTapGesture {
// Any actions here.
}
}
在我看来,最干净的方法是使用自定义绑定。 这样,您就可以完全控制切换开关何时实际切换
导入快捷界面
结构切换演示:视图{
@国家私有变量isToggled=false
var body:一些观点{
让绑定=绑定(
获取:{self.isToggled},
设置:{
潜在异步函数($0)
}
)
func potentialAsyncFunction(\uNewState:Bool){
//异步的东西
self.isToggled=newState
}
返回切换(“我的状态”,isOn:绑定)
}
}
这是一个不使用Tappostate的版本
@State private var isDisplayed = false
Toggle("", isOn: $isDisplayed)
.onReceive([self.isDisplayed].publisher.first()) { (value) in
print("New value is: \(value)")
}
以防你不想使用额外的函数,把结构搞得一团糟——使用状态,并在你想要的任何地方使用它。我知道这不是事件触发器的100%答案,但是,状态将以最简单的方式保存和使用
struct PWSDetailView : View {
@State private var isToggle1 = false
@State private var isToggle2 = false
var body: some View {
ZStack{
List {
Button(action: {
print("\(self.isToggle1)")
print("\(self.isToggle2)")
}){
Text("Settings")
.padding(10)
}
HStack {
Toggle(isOn: $isToggle1){
Text("Music")
}
}
HStack {
Toggle(isOn: $isToggle1){
Text("Music")
}
}
}
}
}
}
可用于XCode 12
import SwiftUI
struct ToggleView: View {
@State var isActive: Bool = false
var body: some View {
Toggle(isOn: $isActive) { Text(isActive ? "Active" : "InActive") }
.padding()
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
}
我是这样编码的:
Toggle("Title", isOn: $isDisplayed)
.onReceive([self.isDisplayed].publisher.first()) { (value) in
//Action code here
}
更新的代码(Xcode 12,iOS14):
iOS13+
这里有一种更通用的方法,您可以应用于几乎所有内置的视图
的任何绑定
,如选择器、文本字段、切换
扩展绑定{
func didSet(执行:@escaping(Value)->Void)->绑定{
返回绑定(
获取:{self.wrappedValue},
设置:{
self.wrappedValue=$0
执行($0)
}
)
}
}
用法简单
@State-var-isOn:Bool=false
切换(“标题”,isOn:$isOn.didSet{(状态)在
打印(状态)
})
损失14+
@State private var isOn=false
var body:一些观点{
切换(“标题”,isOn:$isOn)
.onChange(of:isOn){u isOn in
///在这里使用。。
}
}
SwiftUI 2
如果您使用的是SwiftUI 2/iOS 14,则可以使用extension Binding {
func didSet(execute: @escaping (Value) -> Void) -> Binding {
Binding(
get: { self.wrappedValue },
set: {
self.wrappedValue = $0
execute($0)
}
)
}
}
extension Toggle where Label == Text {
/// Creates a toggle that generates its label from a localized string key.
///
/// This initializer creates a ``Text`` view on your behalf, and treats the
/// localized key similar to ``Text/init(_:tableName:bundle:comment:)``. See
/// `Text` for more information about localizing strings.
///
/// To initialize a toggle with a string variable, use
/// ``Toggle/init(_:isOn:)-2qurm`` instead.
///
/// - Parameters:
/// - titleKey: The key for the toggle's localized title, that describes
/// the purpose of the toggle.
/// - isOn: A binding to a property that indicates whether the toggle is
/// on or off.
/// - onToggled: A closure that is called whenver the toggle is switched.
/// Will not be called on init.
public init(_ titleKey: LocalizedStringKey, isOn: Binding<Bool>, onToggled: @escaping (Bool) -> Void) {
self.init(titleKey, isOn: isOn.didSet(execute: { value in onToggled(value) }))
}
/// Creates a toggle that generates its label from a string.
///
/// This initializer creates a ``Text`` view on your behalf, and treats the
/// title similar to ``Text/init(_:)-9d1g4``. See `Text` for more
/// information about localizing strings.
///
/// To initialize a toggle with a localized string key, use
/// ``Toggle/init(_:isOn:)-8qx3l`` instead.
///
/// - Parameters:
/// - title: A string that describes the purpose of the toggle.
/// - isOn: A binding to a property that indicates whether the toggle is
/// on or off.
/// - onToggled: A closure that is called whenver the toggle is switched.
/// Will not be called on init.
public init<S>(_ title: S, isOn: Binding<Bool>, onToggled: @escaping (Bool) -> Void) where S: StringProtocol {
self.init(title, isOn: isOn.didSet(execute: { value in onToggled(value) }))
}
}
@State var isDisplayed: Bool
Toggle("some text", isOn: .init(
get: { isDisplayed },
set: {
isDisplayed = $0
print("changed")
}
))