五、iWriter 分割視圖的實現(xiàn)

Hi, 大家好,我是姜友華。上一節(jié)我們實現(xiàn)了標(biāo)簽視圖,這一節(jié)我們來實現(xiàn)分割視圖。

在UIKit里是有類似的SplitView的,但在SwiftUI里我沒有找到,所以需要自己來實現(xiàn)。你也可以通過封裝`NSSplitViewControllerNSSplitView來實現(xiàn),這個也留到以后去講。

這里對分割視圖的設(shè)計比較簡單:首先是接收一組視圖,按單一方向排列,中間有分割符隔開即可;其次是實現(xiàn)通過拖動割符隔改變視圖的大小。

一、排列的設(shè)計。

排列的處理比較簡單,接收數(shù)組按水平或垂直排列,各區(qū)域的大小按百分比來處理即可。

先看代碼和顯示效果。

    @State var layouts: [AnyView] = []   // 需要排列的對象。
    @State var ratios: [CGFloat] = []
    @State var isHorizontal  = true  // 是否為水平排列。
    
    var body: some View {
        ZStack{
        GeometryReader{ geometry in
            if isHorizontal {
                HStack(spacing: 0) {
                    ForEach(Array(layouts.enumerated()), id: \.offset) { index, layout in
                        layout
                            .frame( width: frameWidth(index, geometry.size.width))
                        if index < layouts.count - 1 {
                            dividerView(index, geometry.size)
                        }
                    }
                }
            } else {
                VStack(spacing: 0) {
                    ForEach(Array(layouts.enumerated()), id: \.offset) { index, layout in
                        layout
                            .frame( height: frameHeight(index, geometry.size.width))
                        if index < layouts.count - 1 {
                            dividerView(index, geometry.size)
                        }
                    }
                }
            }
        }
        }
    }
    
    func frameWidth(_ index: Int, _ width: CGFloat) -> CGFloat {
        return (width - 10 ) * ratios[index]
    }
    
    func frameHeight(_ index: Int, _ height: CGFloat) -> CGFloat {
        return (height - 1) * ratios[index]
    }
    
    // 分割線。
    func dividerView(_ index: Int, _ size: CGSize) -> some View {
        ......
    }
    
    // 拖動分割線。
    func splitDrag(size: CGSize, current: Int) -> some Gesture {
       ......
            }
    }
}

struct SplitView_Previews: PreviewProvider {
    static var previews: some View {
        SplitView(layouts: [
            AnyView(Text("Window 1").background(.red)),
            AnyView(Text("Window 2").background(.red))
        ], ratios: [0.5, 0.5])
    }
}

效果

這個比較好理解,按水平或垂直排列視圖。水平的按寬設(shè)置占比,垂直的按高設(shè)置占比。這里又用到了GeometryReader,用來獲取布局的尺寸。

需要說明的是,視圖寬高的計算return (width - 10 ) * ratios[index],都是減去一個常量再按占比算,這個常量是分割線的寬。

二、實現(xiàn)分割線的拖動。

分割線的拖動只需要按水平或垂直方向去處理。拖動時動態(tài)修改分割線的位置和視圖的大小。
直接看代碼。

struct SplitView: View {
......
    // 分割線。
    func dividerView(_ index: Int, _ size: CGSize) -> some View {
        Divider()
            .frame(width:1)
            .onHover { inside in
                // 鼠標(biāo)風(fēng)絡(luò)。
                if inside {
                    if isHorizontal {
                        NSCursor.resizeLeftRight.push()
                    } else {
                        NSCursor.resizeUpDown.push()
                    }
                } else {
                    NSCursor.pop()
                }
            }
            .gesture(
                splitDrag(size: size ,current: index)
            )
    }
    
    // 拖動分割線。
    func splitDrag(size: CGSize, current: Int) -> some Gesture {
        DragGesture()
            .onChanged { value in
                // 分割線所有的百分比。
                let value = isHorizontal ? value.location.x : value.location.y
                let toStart = value < 0
                let offset = abs(value) / (isHorizontal ? size.width : size.height)
                let minSize = splitMinSize / (isHorizontal ? size.width : size.height)
                var result = offset
                var array: [CGFloat] = []
                ratios.forEach{ ratio in
                    array.append(ratio)
                }
                
                // 向左或上。
                if toStart {
                    for i in stride(from: current, through: 0, by: -1) {
                        if result < 0.001 {
                            break
                        }
                        // 足夠。
                        if array[i] > result + minSize  {
                            array[i] -= result
                            result = 0
                            break
                        }
                        // 不夠,留最小。
                        let value = array[i] - minSize
                        array[i] = minSize
                        result -= value
                    }
                    if offset > result + 0.001 {
                        array[current + 1] += (offset - result)
                    }
                    ratios = array
                    return
                }
                
                // 向右或下。
                for i in stride(from: current + 1, to: array.count, by: 1) {
                    if result < 0.001 {
                        break
                    }
                    // 足夠。
                    if array[i] > result + minSize  {
                        
                        array[i] -= result
                        result = 0
                        break
                    }
                    // 不夠,留最小。
                    let value = array[i] - minSize
                    array[i] = minSize
                    result -= value
                }
                if offset > result + 0.001 {
                    array[current] += (offset - result)
                }
                ratios = array
            }
    }
}

拖動的處理分兩步:

  • 首先是添加分割線的拖曳事件,這是一種固定處理,即添加 DragGesture對象。
  • 其次是處理onChanged事件,這里的處理是,向移動的方向遂個處理有可能被縮小的視圖,保證每個視圖能以最小值顯示。處理完成后再處理影響放大的那一個即臨近分割線并與移動方向相反的那個。

好,這個也就這些,我是姜友華,下次見

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 0.緒論 開始之前,先來個效果圖 這就是釘釘里面的一天日程安排視圖,主要功能點(diǎn)是: 顯示一天內(nèi)0到24小時具體的每...
    斯卡閱讀 4,877評論 7 7
  • 為大家梳理一個web表格設(shè)計框架,希望能夠給大家?guī)硗暾牟灰粯拥膬?nèi)容。全文12,598字 ,預(yù)計閱讀30分鐘,建...
    小龍ha閱讀 2,084評論 0 3
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,995評論 2 59
  • 使用 Windows 標(biāo)準(zhǔn)控件 為了提高常用代碼的復(fù)用性,VC 使用控件將常用的諸如用戶輸入、操作數(shù)據(jù)等功能封裝起...
    御承揚(yáng)閱讀 2,076評論 0 1
  • 在后臺管理系統(tǒng)、數(shù)據(jù)類產(chǎn)品等的設(shè)計中,表格作為一種常見的信息組織整理手段,甚至是web頁面的基礎(chǔ)設(shè)施之一,其重要性...
    停停走走UP閱讀 5,760評論 3 46

友情鏈接更多精彩內(nèi)容