Swift UIKit 可拖拉按鈕,UIButton-UIPanGestureRecognizer、buttonPressed、buttonReleased

Ahri
彼得潘的 Swift iOS / Flutter App 開發教室
6 min readJul 15, 2024

# move the button

在觸發按鈕時,可以選擇或是buttonTapped或是buttonPressed、buttonReleased

我會選擇buttonPressedbuttonReleased 。是因為按下去會改變按鈕顏色。對只是這樣的原因。

但是UIPanGestureRecognizer跟buttonPressed、buttonReleased需要設條件去『分別啟動』,不然他們會打架,會有一方輸然後躺在地上不動…

我的整體思路:

建立按鈕 > 加入UIPanGestureRecognizer > 設定要幾指移動、移動範圍 > 移動結束後如有按按鈕 > 再加入並連接到buttonPressed、buttonReleased

以下是程式碼:


// 創建按鈕
circularButton = UIButton(type: .custom)
// 設置按鈕大小
circularButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44)

// 設置按鈕圓形
circularButton.layer.cornerRadius = 22 // 4ˋ的一半
circularButton.clipsToBounds = true

// 設置按鈕圖片填滿模式
circularButton.imageView?.contentMode = .scaleToFill

// 設置按鈕位置,在螢幕中央的最下方
circularButton.center = CGPoint(x: view.bounds.midX, y: view.bounds.maxY - 44 / 2 - 20) // 20 是距離底部的距離,可以根據需要調整

// 添加按鈕點擊事件,拖動手勢識別器
circularButton.addTarget(self, action: #selector(buttonPressed), for: .touchDown)//暫時沒寫功能但保留
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
circularButton.addGestureRecognizer(panGestureRecognizer)


@objc func buttonPressed(_ sender: UIButton) {
//按鈕按下去時的動作,可以設定換圖片,換顏色...
}

@objc func buttonReleased(_ sender: UIButton) {
// 放開按鈕後的動作

}

// 處理拖動的方法
@objc func handlePan(_ recognizer: UIPanGestureRecognizer) {

let translation = recognizer.translation(in: view)
if let button = recognizer.view {
var newCenter = CGPoint(x: button.center.x + translation.x, y: button.center.y + translation.y)

// 限制按鈕的寬位置不超出父視圖的邊界
let halfWidth = button.frame.width / 2
newCenter.x = max(halfWidth, newCenter.x)
newCenter.x = min(view.bounds.size.width - halfWidth, newCenter.x)

//限制按鈕高度
let screenHeight = UIScreen.main.bounds.height
let halfHeight = button.frame.height / 2
//按鈕要低於navigationBar
let navigationBarHeight = navigationController?.navigationBar.frame.size.height ?? 44
//且低於狀態欄
let statusBarHeight = UIApplication.shared.statusBarFrame.height
//且低於searchBar,下條件是為了調整按鈕在小螢幕(iphoneSE)上的狀態
var searchBarHeight = 22
if screenHeight < 852{
searchBarHeight = 55
}else{
searchBarHeight = 22
}
newCenter.y = max(halfHeight + navigationBarHeight + statusBarHeight + CGFloat(searchBarHeight), newCenter.y) // 將導航欄和狀態欄高度納入計算
newCenter.y = min(view.bounds.size.height - halfHeight - 10, newCenter.y)

// 更新按鈕的中心位置
button.center = newCenter
}
recognizer.setTranslation(CGPoint.zero, in: view) // 重置拖動距離

//判斷是否完成拖拽,決定是否要啟動buttonReleased
if recognizer.state == .ended {
circularButton.addTarget(self, action: #selector(buttonReleased), for: [.touchUpInside, .touchUpOutside, .touchCancel])
} else if recognizer.state == .began {
circularButton.removeTarget(self, action: #selector(buttonReleased), for: [.touchUpInside, .touchUpOutside, .touchCancel])
}
}

參考資料

寫程式就是要腦洞大開,想到什麼路就試試看,一切皆有可能。

--

--