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

Hi, 大家好乳幸,我是姜友華。上一節(jié)我們實現(xiàn)了標簽視圖钧椰,這一節(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
                // 鼠標風絡(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)系作者
  • 序言:七十年代末侯谁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子章钾,更是在濱河造成了極大的恐慌墙贱,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贱傀,死亡現(xiàn)場離奇詭異惨撇,居然都是意外死亡,警方通過查閱死者的電腦和手機府寒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門魁衙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人株搔,你說我怎么就攤上這事剖淀。” “怎么了纤房?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵纵隔,是天一觀的道長。 經(jīng)常有香客問我炮姨,道長捌刮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任剑令,我火速辦了婚禮糊啡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吁津。我一直安慰自己棚蓄,他們只是感情好堕扶,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著梭依,像睡著了一般稍算。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上役拴,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天糊探,我揣著相機與錄音,去河邊找鬼河闰。 笑死科平,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的姜性。 我是一名探鬼主播瞪慧,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼部念!你這毒婦竟也來了弃酌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤儡炼,失蹤者是張志新(化名)和其女友劉穎妓湘,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乌询,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡榜贴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了楣责。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竣灌。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖秆麸,靈堂內(nèi)的尸體忽然破棺而出初嘹,到底是詐尸還是另有隱情,我是刑警寧澤沮趣,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布屯烦,位于F島的核電站,受9級特大地震影響房铭,放射性物質(zhì)發(fā)生泄漏驻龟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一缸匪、第九天 我趴在偏房一處隱蔽的房頂上張望翁狐。 院中可真熱鬧,春花似錦凌蔬、人聲如沸露懒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽懈词。三九已至蛇耀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坎弯,已是汗流浹背纺涤。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留抠忘,地道東北人撩炊。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像崎脉,于是被迫代替她去往敵國和親衰抑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 0.緒論 開始之前,先來個效果圖 這就是釘釘里面的一天日程安排視圖,主要功能點是: 顯示一天內(nèi)0到24小時具體的每...
    斯卡閱讀 4,505評論 7 7
  • 為大家梳理一個web表格設(shè)計框架荧嵌,希望能夠給大家?guī)硗暾牟灰粯拥膬?nèi)容。全文12,598字 砾淌,預(yù)計閱讀30分鐘啦撮,建...
    小龍ha閱讀 1,860評論 0 3
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料汪厨? 從這篇文章中你...
    hw1212閱讀 12,693評論 2 59
  • 前言:--- 細講一下赃春,頂部視圖下拉放大,其中有很多細節(jié)非常值得學(xué)習(xí)劫乱,比如說织中,分割線的設(shè)置,狀態(tài)欄的設(shè)...
    麥穗0615閱讀 945評論 5 11
  • 使用 Windows 標準控件 為了提高常用代碼的復(fù)用性衷戈,VC 使用控件將常用的諸如用戶輸入狭吼、操作數(shù)據(jù)等功能封裝起...
    御承揚閱讀 1,881評論 0 1