大家好,我是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 佈局剖析
下面以悦記APP中NoteView中a_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 - 直接交流開發心得