Stories

Detail Return Return

4.佈局系統 - Stories Detail

大家好,我是K哥。一名獨立開發者,同時也是Swift開發框架【Aquarius】的作者,悦記愛尋車app的開發者。

Aquarius開發框架旨在幫助獨立開發者和中小型團隊,完成iOS App的快速實現與迭代。使用框架開發將給你帶來簡單、高效、易維護的編程體驗。


Aquarius佈局系統簡介

Aquarius開發框架提供了一套完整的、極簡的佈局系統。通過該佈局系統,你可以輕鬆的完成基於代碼控制的視圖佈局。

核心價值

  • 🎯 一行頂多行 - 極簡API,大幅減少代碼量
  • 動效零成本 - 所有佈局變化天然支持動畫
  • 🔗 關係式佈局 - 直觀表達視圖間相對關係
  • 📦 批量好幫手 - 一次性操作多個視圖
  • 🔄 無縫兼容 - 基於原生frame,即插即用

系統特點

  • 直觀的定位和大小調整方法
  • 內置動畫支持
  • 多視圖批量操作

參見源碼

  • UIView+aLayout.swift
  • Array+aLayout.swift

複雜度:

  • 基礎佈局:提供了對控件的基礎設置
  • 高級佈局:提供了控件間關係型的動態設置

基礎佈局:重塑單個視圖的操控體驗

處理單個視圖

位置

使用框架提供的方法,你可以輕鬆的完成視圖位置的獲取和設置。

  • 獲取x位置

不使用框架的獲取方式

let x: CGFloat = myView.frame.origin.x

使用框架的獲取方式

let x: CGFloat = myView.x()
//或
let x: CGFloat = myView.left()
  • 設置x位置

不使用框架的獲取方式

myView.frame.origin.x = 10.0

使用框架的獲取方式

myView.x(x: 10.0)
//或
myView.left(left: 10.0)
  • 獲取y位置

不使用框架的獲取方式

let y: CGFloat = myView.frame.origin.y

使用框架的獲取方式

let y: CGFloat = myView.y()
//或
let y: CGFloat = myView.top()
  • 設置y位置

不使用框架的獲取方式

myView.frame.origin.y = 10.0

使用框架的獲取方式

myView.y(y: 10.0)
//或
myView.top(top: 10.0)
  • 獲取右側位置

不使用框架的獲取方式

let right: CGFloat = myView.frame.origin.x + myView.frame.size.width

使用框架的獲取方式

let right: CGFloat = myView.right()
  • 設置右側位置

不使用框架的獲取方式

myView.frame.origin.x = 200.0 - myView.frame.size.width

使用框架的獲取方式

myView.right(right: 200)
  • 獲取底部位置

不使用框架的獲取方式

let bottom: CGFloat = myView.frame.origin.y + myView.frame.size.height

使用框架的獲取方式

let bottom: CGFloat = myView.bottom()
  • 設置底部位置

不使用框架的獲取方式

myView.frame.origin.y = 200.0 - myView.frame.size.height

使用框架的獲取方式

myView.bottom(bottom: 200.0)

大小

使用框架提供的方法,你可以輕鬆的完成視圖大小的獲取和設置。

  • 獲取寬度

不使用框架的獲取方式

let width: CGFloat = myView.frame.size.width

使用框架的獲取方式

let width: CGFloat = myView.width()
  • 設置寬度

不使用框架的獲取方式

myView.frame.size.width = 100.0

使用框架的獲取方式

myView.width(width: 100.0)
  • 獲取高度

不使用框架的獲取方式

let height: CGFloat = myView.frame.size.height

使用框架的獲取方式

let height: CGFloat = myView.height()
  • 設置高度

不使用框架的獲取方式

myView.frame.size.height = 100.0

使用框架的獲取方式

myView.height(height: 100.0)

point

  • 獲取point

不使用框架的獲取方式

let point: CGPoint = myView.frame.origin

使用框架的獲取方式

let point: CGPoint = myView.point()
  • 設置point

不使用框架的獲取方式

myView.frame.origin = CGPoint(x: 10.0, y: 10.0)

使用框架的獲取方式

myView.point(x: 10.0, y: 10.0)
//或
myView.point(point: CGPoint(x: 10.0, y: 10.0)
//或
myView.point(points: [10.0, 10.0])
//當x, y值相同時
myView.point(xy: 10.0)

Size

  • 獲取Size

不使用框架的獲取方式

let size: CGSize = myView.frame.size

使用框架的獲取方式

let size: CGSize = myView.size()
  • 設置Size

不使用框架的獲取方式

myView.frame.size = CGSize(width: 100.0, height: 100.0)

使用框架的獲取方式

myView.size(width: 100.0, height: 100.0)
//或
myView.size(w: 100.0, h: 100.0)
//或
myView.size(size: CGSize(width: 100.0, height: 100.0))
//或
myView.size(sizes: [100.0, 100.0])
//當寬和高相同時
myView.size(widthHeight: 100.0)

frame

  • 獲取frame

不使用框架的獲取方式

let frame: CGRect = myView.frame

使用框架的獲取方式

let frame: CGRect = myView.frame()
  • 設置frame

不使用框架的獲取方式

myView.frame = CGRect(x: 10.0, y: 10.0, width: 100.0, height: 100.0)
//或
myView.frame = CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: CGSize(width: 100.0, height: 100.0))

使用框架的獲取方式

myView.frame(frame: CGRect(x: 10.0, y: 10.0, width: 100.0, height: 100.0))
//或
myView.frame(x: 10.0, y: 10.0, w: 100.0, h: 100.0)
//或
myView.frame(frames: [10.0, 10.0, 100.0, 100.0])
//當x, y和寬、高相同時
myView.frame(xy: 10.0, widthHeight: 100.0)

處理多個視圖

基礎操作

  • 設置多個視圖相同的x值

不使用框架的獲取方式

view1.frame.origin.x = 10.0
view2.frame.origin.x = 10.0

使用框架的獲取方式

UIView.x(x: 10.0, views: [view1, view2])
//或
UIView.left(left: 10.0, views: [view1, view2])
  • 設置多個視圖相同的right值

使用框架的獲取方式

UIView.right(right: 10.0, views: [view1, view2])
  • 設置多個視圖相同的y值

不使用框架的獲取方式

view1.frame.origin.y = 10.0
view2.frame.origin.y = 10.0

使用框架的獲取方式

UIView.y(y: 10.0, views: [view1, view2])
//或
UIView.top(top: 10.0, views: [view1, view2])
  • 設置多個視圖相同的bottom值

使用框架的獲取方式

UIView.bottom(bottom: 10.0, views: [view1, view2])
  • 設置多個視圖相同的寬度

不使用框架的獲取方式

view1.frame.size.width = 100.0
view2.frame.size.width = 100.0

使用框架的獲取方式

UIView.width(width: 100.0, views: [view1, view2])
  • 設置多個視圖相同的高度

不使用框架的獲取方式

view1.frame.size.height = 100.0
view2.frame.size.height = 100.0

使用框架的獲取方式

UIView.height(height: 100.0, views: [view1, view2])
  • 設置多個視圖相同的point

不使用框架的獲取方式

view1.frame.origin = CGPoint(x: 10, y: 10)
view2.frame.origin = CGPoint(x: 10, y: 10)

使用框架的獲取方式

UIView.point(x: 10.0, y: 10.0, views: [view1, view2])
//或
UIView.point(point: CGPoint(x: 10.0, y: 10.0), views: [view1, view2])
//或
UIView.point(points: [10.0, 10.0], views: [view1, view2])
//當x, y值相同時
UIView.point(xy: 10.0, views: [view1, view2])
  • 設置多個視圖相同的Size

不使用框架的獲取方式

view1.frame.size = CGSize(width: 100.0, height: 100.0)
view2.frame.size = CGSize(width: 100.0, height: 100.0)

使用框架的獲取方式

UIView.size(width: 100.0, height: 100.0, views: [view1, view2])
//或
UIView.size(w: 100.0, h: 100.0, views: [view1, view2])
//或
UIView.size(size: CGSize(width: 100.0, height: 100.0), views: [view1, view2])
//或
UIView.size(sizes: [100.0, 100.0], views: [view1, view2])
//當寬和高相同時
UIView.size(widthHeight: 100.0, views: [view1, view2])
  • 設置多個視圖相同的frame

不使用框架的獲取方式

view1.frame = CGRect(x: 10.0, y: 10.0, width: 100.0, height: 100.0)
view2.frame = CGRect(x: 10.0, y: 10.0, width: 100.0, height: 100.0)
//或
view1.frame = CGRect(origin: CGPoint(x: 10.0, y: 10.0), size:CGSize(width: 100.0, height: 100.0))
view2.frame = CGRect(origin: CGPoint(x: 10.0, y: 10.0), size:CGSize(width: 100.0, height: 100.0))

使用框架的獲取方式

UIView.frame(x: 10.0, y: 10.0, w: 100.0, h: 100.0, views: [view1, view2])
//或
UIView.frame(frame: CGRect(x: 10.0, y: 10.0, width: 100.0, height: 100.0), views: [view1, view2])
//或
UIView.frame(frame: CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: CGSize(width: 100.0, height: 100.0)), views: [view1, view2])
//或
UIView.frame(frames: [10.0, 10.0, 100.0, 100.0], views: [view1, view2])
//當x, y和寬, 高相等時
UIView.frame(xy: 10.0, widthHeight: 100.0, views: [view1, view2])

對齊操作

框架支持多個視圖的對齊

//頂端對齊
UIView.top(views: [view1, view2, view3])
//底部對齊
UIView.bottom(views: [view1, view2, view3])
//左側對齊
UIView.left(views: [view1, view2, view3])
//右側對齊
UIView.right(views: [view1, view2, view3])

支持對齊到某個目標位置

//頂端對齊
UIView.top(views: [view1, view2, view3], position: 50)
//底部對齊
UIView.bottom(views: [view1, view2, view3], position: 50)
//左側對齊
UIView.left(views: [view1, view2, view3], position: 50)
//右側對齊
UIView.right(views: [view1, view2, view3], position: 50)

分佈操作

均勻分佈視圖:

// 在第一個和最後一個視圖之間水平分佈視圖
UIView.horizontal(views: [view1, view2, view3, view4]) 

// 在第一個和最後一個視圖之間垂直分佈視圖
UIView.vertical(views: [view1, view2, view3, view4])

組合對齊和分佈操作

在一次操作中對齊和分佈視圖:

// 頂端對齊並且水平分佈視圖
UIView.topAndHorizontal(views: [view1, view2, view3, view4]) 
// 頂端對齊到某個目標位置並且水平分佈視圖
UIView.topAndHorizontal(views: [view1, view2, view3, view4], position: 50) 
// 底部對齊並且水平分佈視圖
UIView.bottomAndHorizontal(views: [view1, view2, view3, view4]) 
// 底部對齊到某個目標位置並且水平分佈視圖
UIView.bottomAndHorizontal(views: [view1, view2, view3, view4], position: 50) 

// 左側對齊並且垂直分佈視圖
UIView.leftVertical(views: [view1, view2, view3, view4])
// 左側對齊到某個目標位置並且垂直分佈視圖
UIView.leftVertical(views: [view1, view2, view3, view4], position: 50)
// 右側對齊並且垂直分佈視圖
UIView.rightVertical(views: [view1, view2, view3, view4])
// 右側對齊到某個目標位置並且垂直分佈視圖
UIView.rightVertical(views: [view1, view2, view3, view4], position: 50)

批量操作

框架支持數組的方式批量操作視圖

let views: [UIView] = [view1, view2, view3]

views.width(width: 100.0)
views.width(width: 100.0, 1)//設置數組中第2個UIView的寬度

views.height(height: 100.0)
views.height(height: 100.0, 1)//設置數組中第2個UIView的高度

高級佈局:構建智能的、響應式的界面

Aquarius開發框架提供了一個強大且靈活的佈局系統,超越了基本的定位功能。通過高級佈局技術,幫助你用最少的代碼和最大的靈活性創建複雜的UI佈局。

Aquarius開發框架通過全面的佈局方法擴展了UIView,這些方法建立在原生基於框架的佈局系統之上,使其更加直觀和強大。與Auto Layout的基於約束的方法不同,Aquarius開發框架提供了直接、命令式的API,易於理解且可以無縫動畫。

相對定位

基礎操作

Aquarius開發框架最強大的功能之一是它能夠將視圖相對於彼此定位。這消除了在排列視圖時進行復雜計算的需要。

// 將viewB定位在viewA下方,間距為10pt
viewB.alignTop(view: viewA, offset: 10)

// 將viewB定位在viewA右側,間距為15pt
viewB.alignLeft(view: viewA, offset: 15)

// 將viewB定位在viewA左側,間距為8pt
viewB.alignRight(view: viewA, offset: 8)

// 將viewB定位在viewA上方,間距為12pt
viewB.alignBottom(view: viewA, offset: 12)

批量操作

let views: [UIView] = [view1, view2, view3]

// 將views數組定位在viewA下方,間距為8pt
views.alignTop(view: viewA, offset: 8)

// 將views數組中第2個視圖定位在viewA下方,間距為8pt
views.alignTop(view: viewA, offset: 8, 1)

// 將views數組定位在viewA上方,間距為8pt
views.alignBottom(view: viewA, offset: 8)

// 將views數組中第2個UI視圖在viewA上方,間距為8pt
views.alignBottom(view: viewA, offset: 8, 1)

// 將views數組定位在viewA右側,間距為8pt
views.alignLeft(view: viewA, offset: 8)

// 將views數組中第2個視圖視圖iewA右側,間距為8pt
views.alignLeft(view: viewA, offset: 8, 1)

// 將views數組定位在viewA左側,間距為8pt
views.alignRight(view: viewA, offset: 8)

// 將views數組中第2個UI視圖視圖wA左側,間距為8pt
views.alignRight(view: viewA, offset: 8, 1)

這些方法簡化了流佈局的創建,並能夠在處理動態內容時無需手動計算位置。

等同定位

基礎操作

當希望視圖共享相同的邊緣位置時,equal方法提供了一個簡潔的語法

// 使viewB的左邊緣與viewA相同(可選偏移)
viewB.equalLeft(target: viewA, offset: 5)

// 使viewB的右邊緣與viewA相同
viewB.equalRight(target: viewA)

// 使viewB的頂部邊緣與viewA相同
viewB.equalTop(target: viewA)

// 使viewB的底部邊緣與viewA相同
viewB.equalBottom(target: viewA)

// 使viewB的大小與viewA相同
viewB.equalSize(target: viewA)

// 使viewB的框架與viewA相同
viewB.equalRect(target: viewA)

// 使viewB的左側邊緣設置為0
viewB.equalZeroLeft()

// 使viewB的頂端邊緣設置為0
viewB.equalZeroTop()

// 使viewB的左側邊緣和頂端邊緣設置為0
viewB.equalZeroTopAndLeft()

批量操作

let views: [UIView] = [view1, view2, view3]

// 使views數組的左邊緣與viewA相同,間距為5pt
views.equalLeft(target: viewA, offset: 5)

// 使views數組中第2個視圖的左邊緣與viewA相同,間距為5pt
views.equalLeft(target: viewA, offset: 5, 1)

// 使views數組的右邊緣與viewA相同,間距為5pt
views.equalRight(target: viewA, offset: 5)

// 使views數組中第2個UI視圖邊緣與viewA相同,間距為5pt
views.equalRight(target: viewA, offset: 5, 1)

// 使views數組的寬度與viewA相同,間距為5pt
views.equalWidth(target: viewA, offset: 5)

// 使views數組中第2個視圖視圖viewA相同,間距為5pt
views.equalWidth(target: viewA, offset: 5, 1)

// 使views數組的高度與viewA相同,間距為5pt
views.equalHeight(target: viewA, offset: 5)

// 使views數組中第2個UI視圖視圖ewA相同,間距為5pt
views.equalHeight(target: viewA, offset: 5, 1)

這種方法非常適合創建對齊的UI元素,並在界面中保持視覺和諧。

屏幕感知定位

Aquarius開發框架提供了方便的方法來處理屏幕尺寸和系統UI元素:

// 設置視圖寬度與屏幕寬度相同
view.equalScreenWidth()

// 設置視圖高度與屏幕高度相同
view.equalScreenHeight()

// 設置視圖寬度和高度與屏幕高度相同
view.equalScrenSize()

// 設置視圖高度與屏幕高度相同,減去狀態欄高度
view.equalScreenHeightNoStatus()

// 設置視圖高度與屏幕高度相同,減去導航欄高度
view.screenHeightNoNavigation()

// 設置視圖高度與屏幕高度相同,減去狀態欄和導航欄高度
view.screenHeightNoStatusNoNavigation()

// 設置視圖高度與屏幕高度相同,減去底部安全區高度
view.screenHeightNoSafeAreaFooter()

// 設置視圖高度與屏幕高度相同,減去tabBar高度
view.screenHeightNoTabBar()

// 設置視圖高度與屏幕高度相同,減去狀態欄和底部安全區高度
view.screenHeightNoStatusNoSafeAreaFooter()

// 設置視圖高度與屏幕高度相同,減去狀態欄和tabBar高度
view.screenHeightNoStatusNoTabBar()

// 設置視圖高度與屏幕高度相同,減去狀態欄、底部安全區和tabBar高度
view.screenHeightNoStatusNoSafeAreaFooterNoTabBar()

// 設置視圖高度與屏幕高度相同,減去導航欄和底部安全區高度
view.screenHeightNoNavigationNoSafeAreaFooter()

// 設置視圖高度與屏幕高度相同,減去導航欄和tabBar高度
view.screenHeightNoNavigationNoTabBar()

// 設置視圖高度與屏幕高度相同,減去導航欄、底部安全區和tabBar高度
view.screenHeightNoNavigationNoSafeAreaFooterNoTabBar()

// 設置視圖高度與屏幕高度相同,減去狀態欄、導航欄和底部安全區高度
view.screenHeightNoStatusNoNavigationNoSafeAreaFooter()

// 設置視圖高度與屏幕高度相同,減去狀態欄、導航欄和tabBar高度
view.screenHeightNoStatusNoNavigationNoTabBar()

// 設置視圖高度與屏幕高度相同,減去狀態欄、導航欄、底部安全區和tabBar高度
view.screenHeightNoStatusNoNavigationNoSafeAreaFooterNoTabBar()

動畫集成:讓界面“活”起來

Aquarius開發框架針對佈局系統提供簡單的動畫,所有佈局系統相關的方法均提供動畫功能

// 在0.3秒內動畫改變寬度
view.width(width: 200, animate: true, duration: 0.3)

// 使用默認動畫時長改變位置
view.left(left: 50, animate: true)

// 使用自定義時序動畫多個視圖到相同位置
UIView.bottom(bottom: 500, animate: true, duration: 0.5, views: [view1, view2, view3])
...

這種內置的動畫支持使創建佈局狀態之間的平滑過渡變得簡單,而無需額外代碼。

實戰案例:悦記 NoteView 佈局剖析

下面以悦記APPNoteViewa_Layout方法為例,展示Aquarius開發框架佈局系統的具體使用案例:

import UIKit
import Foundation

import Aquarius
import CommonFramework

class NoteView: BaseView {
    ...
    override func a_Layout() {
        super.a_Layout()

        searchBar.frame(frames: [
                8,
                8,
                screenWidth()-8*2,
                52
            ])

        noteTableView.size(sizes: [
                screenWidth(),
                screenHeight()-navigationBarHeight()-safeAreaFooterHeight()-tabBarHeight()-8
            ])
        noteTableView.equalTop(target: searchBar)
        noteTableView.equalLeft(target: self)

        footerView.equalWidth(target: noteTableView)
        footerView.height(height: 48.0)

        activityIndicatorView.equalSize(target: footerView)
        activityIndicatorView.equalZeroTopAndLeft()

        leftSpaceView.width(width: NoteCell.distance)
        leftSpaceView.equalHeight(target: noteTableView)
        leftSpaceView.equalZeroLeft()
        leftSpaceView.equalTop(target: noteTableView)
        leftSpaceView.target(rightSpaceView)
        leftSpaceView.equals([.size, .top])

        rightSpaceView.left(left: screenWidth()-NoteCell.distance)

        createNoteButton.size(widthHeight: 60)
        createNoteButton.point(points: [
                screenWidth()-60-16,
                screenHeight()-statusBarHeight()-navigationBarHeight()-tabBarHeight()-safeAreaFooterHeight()-createNoteButton.height()
            ])
    }
    ...
}

代碼解讀

  • 關係清晰:視圖間的依賴關係(如 equalTop, equalWidth)讓佈局邏輯一目瞭然。
  • 易於維護:當某個視圖的尺寸或位置需要調整時,只需修改一處,相關視圖會自動更新。
  • 動態適應:使用 screenHeight(), navigationBarHeight() 等方法,佈局能自動適應不同的設備尺寸和系統狀態。

為什麼選擇Aquarius佈局系統

特性 Aquarius 原生Frame AutoLayout
代碼簡潔性 🏆 極致簡潔 重複繁瑣 冗長複雜
學習成本 🏆 幾分鐘上手 較低 曲線陡峭
動畫支持 🏆 原生內置 需手動實現 實現複雜
運行性能 🏆 原生級性能 最佳 佈局計算開銷

適用場景

  • 🎯 獨立開發者:追求開發效率,希望快速迭代。
  • 🎯 動態UI:界面元素需要頻繁變化、動畫豐富的應用。
  • 🎯 代碼控:喜歡用代碼精確控制每一個像素,厭惡Storyboard的臃腫。
  • 🎯 遷移項目:老項目基於Frame,希望用最小成本引入現代佈局方式。

總結

Aquarius開發框架的佈局系統提供了Auto Layout的強大替代方案,強調可讀性、簡潔性和動畫集成。通過本篇文章介紹的相關技術,你可以用更少的代碼創建複雜的佈局,同時保持對UI元素定位和動畫的完全控制。

直觀的相對定位、靈活的分佈方法和內置的動畫支持相結合,使Aquarius開發框架成為需要創建動態、響應式界面的開發者的絕佳選擇,而無需約束佈局的複雜性。


立即體驗Aquarius:

第一步:探索資源

  • ⭐ Star & Fork 框架源碼: GitHub - JZXStudio/Aquarius - 支持項目發展
  • ⭐ Star & Fork 悦記源碼: GitHub - JZXStudio/yuenote - 完整案例,深入瞭解框架使用方式

第二步:體驗效果

  • 📱 下載示例APP: 悦記 | 愛尋車 - 感受真實項目中的流暢體驗

第三步:溝通交流

  • 💬 提交Issue: GitHub Issues - 反饋問題或建議
  • 💌 聯繫與反饋: studio_jzx@163.com - 直接交流開發心得
user avatar 323duqpq Avatar someonelikeyou Avatar chiqingdechouti Avatar jiuliangxiaodeyaling Avatar GarveyCalvin Avatar CoolBreeze-SeclusionDream Avatar
Favorites 6 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.