飞道的博客

SwiftUI重构功能视图: Never Repeat Yourself!

384人阅读  评论(0)

1.原来视图的架构:我们需要监听一个通知

父VC弹出一个子View,其中子View需要接收父VC发出的通知:

[父VC]--------弹出-------->[View]

[View]--------监听-------->[父VC]

由于View的特殊性,我们不能直接将监听回调放在View本身,而是要另外写一个类似协调器的东东:

class Coordinator {

        let slaView:SLAView
        
        init(_ slaView:SLAView){
            self.slaView = slaView
        }
        
        @objc func volChanged(_ notify: NSNotification){
            if let info = notify.userInfo as? [String:Float]{
                let valString = String(format: "%.2f ", info["Vol"]!)
                slaView.text += valString
            }
        }
    }

下面是子View的代码:

@available(iOS 13.0, *)
struct SLAView: View {
    
    @State var text = ""

	/// 上面协调器类的定义在这里(省略之) ;)
     
    weak var rcsMgr: RCSManager?
    var dismiss: (()->Void)? = nil
        
    @State private var _coordinator:Coordinator!
    private var coordinator:Coordinator{
        if _coordinator == nil{
            _coordinator = Coordinator(self)
        }
        return _coordinator
    }
    
    var body: some View {
                
        VStack {
            /// 视图的内容,你不用操心哦 ;)
        }.onAppear {
            NotificationCenter.default.addObserver(self.coordinator, selector: #selector(Coordinator.volChanged(_:)), name: NSNotification.Name.VolumeDidChanged, object: nil)
        }.onDisappear {
            NotificationCenter.default.removeObserver(self)
        }
    }
}

看上去貌似很好,不过现在我们需要另外写一个其他功能的View,该View同样需要监听父VC发出的通知。

如果不对Views的代码进行重新组织,我们很快就会在Repeat Self可耻的路上越走越远了…

所以我们必须做出改变!!!

2.一个专用的监听View

现在我们把所需的监听功能统一放在一个单独的View中。

创建一个新的View: VolumeChangeListenerView

struct VolumeChangeListenerView<Content>: View where Content:View{

}

因为View的内容和对通知的处理需要由使用者决定,所以我们接下来在View定义中创建两个与之对应的闭包属性:

var content:()->Content
var volumeDidChanged:(Float)->()

我们现在可以把协调器的定义以及其对应的属性放在VolumeChangeListenerView里了,
只不过协调器中对于监听的处理需要略作修改,代码如下:

class Coordinator {

        let view:VolumeChangeListenerView
        
        init(_ view:VolumeChangeListenerView){
            self.view = view
        }
        
        @objc func volChanged(_ notify: NSNotification){
            if let info = notify.userInfo as? [String:Float]{
            	// 实际处理我们交给view来做
                view.volumeDidChanged(info["Vol"]!)
            }
        }
    }

现在到了最关键的body代码了,我们基本可以轻车熟路的这样写:

var body: some View {
        content()
            .onAppear {
                NotificationCenter.default.addObserver(self.coordinator, selector: #selector(Coordinator.volChanged(_:)), name: NSNotification.Name.VolumeDidChanged, object: nil)
            }.onDisappear {
                NotificationCenter.default.removeObserver(self)
            }
    }

搞定!!!

3.重构原来的视图

现在我们有了新的VolumeChangeListenerView视图,我们可以将开头的SLAView重构如下:

struct SLAView: View {
    
    @State var text = ""
    weak var rcsMgr: RCSManager?
    var dismiss: (()->Void)? = nil
    
    var body: some View {
        VolumeChangeListenerView(content: {
        	// 原来View中的显示内容在此,你同样不用操心哦 ;)        
        }){vol in
            let valString = String(format: "%.2f ", vol)
            self.text += valString
        }
    }
}

我们同样可以非常轻松的基于VolumeChangeListenerView,创建一个新功能的监听视图了:

struct SLAView2: View {
    
    var body: some View {
        VolumeChangeListenerView(content: {
        	// 显示内容在此,你还是不用操心哦 ;)        
        }){vol in
            // 你想要对vol数据做神马都可以... ;)
        }
    }
}

好啦!这样我们之后无论再写多少个带有监听功能的不同View,都可以直接借助于VolumeChangeListenerView类了。这样简化了代码,消除了重复,你是不是值得拥有呢? 😉

4.结语

就一句话:其实偶尔重构一下挺好的!!!


转载:https://blog.csdn.net/mydo/article/details/103918874
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场