Swift.輪轉動畫,100行程式碼搞定
阿新 • • 發佈:2018-11-09
實現效果:
靜止時:子view對稱排列,允許動態新增,0~24個都能較好的顯示.
旋轉時:中心view不動,子view隨手勢旋轉,最下方子view變大突出.
實現思路:
所有的控制元件全部加到一個大的背景view上,本質上旋轉的是這個背景view,在旋轉背景view的同時,讓它所有的子控制元件反向旋轉,就實現了現在這種效果.
使用touchMoved方法獲取手勢,使用transform來實現動畫.
最下方的view變大是迴圈判斷子view.frame.x,當它處於一個範圍,並且frame.y大於中心view.frame.y的時候.修改它的transform,來使其變大,並且修改它的tag來標記它已經屬於變大狀態,當它frame.x超出了預定範圍,使其還原.
實現方式:
1.新增背景透明view,中心圓圈view.
2.新增周圍旋轉子view.
3.新增旋轉方法.
4.互動優化.
1.新增背景透明view,中心圓圈view.
/// 新增背景view,也是旋轉的view private func setContentView() { setCircleView() contentView = UIView(frame: CGRect(x: 0, y: 0, width: ScreenInfo.Width, height: ScreenInfo.Width)) contentView?.center = self.view.center self.view.addSubview(contentView!) contentView!.addSubview(circleView!) } /// 新增中間圓形view private func setCircleView(){ let view = UIImageView(frame: CGRect(x: 0.5 * CGFloat(1 - PROPORTION) * ScreenInfo.Width + 10, y: 0.5 * CGFloat(1 - PROPORTION) * ScreenInfo.Width + 10, width: ScreenInfo.Width * CGFloat(PROPORTION) - 20, height: ScreenInfo.Width * CGFloat(PROPORTION) - 20)) /// 為了適配保證size變化center不變 let centerPoint = view.center view.frame.size = CGSize(width: ScreenInfo.Width * CGFloat(PROPORTION) - 40, height: ScreenInfo.Width * CGFloat(PROPORTION) - 40) view.center = centerPoint view.image = UIImage(named: "11") view.layer.cornerRadius = view.frame.width*0.5 view.layer.masksToBounds = true view.isUserInteractionEnabled = true circleView = view }
2.新增周圍旋轉子view.
/// 佈局旋轉的子view private func rotationCircleCenter(contentOrgin: CGPoint, contentRadius: CGFloat,subnode: [String]){ // 新增比例,實現當要新增的子view數量較多時候可以自適應大小. var scale: CGFloat = 1 if subnode.count > 10 { scale = CGFloat(CGFloat(subnode.count) / 13.0) } for i in 0..<subnode.count { let x = contentRadius * CGFloat(sin(.pi * 2 / Double(subnode.count) * Double(i))) let y = contentRadius * CGFloat(cos(.pi * 2 / Double(subnode.count) * Double(i))) // 當子view數量大於10個,view.size變小,防止view偏移,要保證view.center不變. let view = EWSubView(frame: CGRect(x:contentRadius + 0.5 * CGFloat((1 + PROPORTION)) * x - 0.5 * CGFloat((1 - PROPORTION)) * contentRadius, y: contentRadius - 0.5 * CGFloat(1 + PROPORTION) * y - 0.5 * CGFloat(1 - PROPORTION) * contentRadius, width: CGFloat((1 - PROPORTION)) * contentRadius, height: CGFloat((1 - PROPORTION)) * contentRadius), imageName: subnode[i]) let centerPoint = view.center view.frame.size = CGSize(width: CGFloat((1 - PROPORTION)) * contentRadius / scale , height: CGFloat((1 - PROPORTION)) * contentRadius / scale) view.center = centerPoint view.drawSubView() // 這個tag判斷view是不是在最下方變大狀態,非變大狀態0,變大為1 view.tag = 0 // 獲取子view在當前螢幕中的rect.來實現在最下方的那個變大 let rect = view.convert(view.bounds, to: UIApplication.shared.keyWindow) let viewCenterX = rect.origin.x + (rect.width) / 2 if viewCenterX > self.view.center.x - 20 && viewCenterX < self.view.center.x + 20 && rect.origin.y > (contentView?.center.y)! { view.transform = view.transform.scaledBy(x: 1.5, y: 1.5) view.tag = 1 } contentView?.addSubview(view) viewArray.append(view) } }
3.新增旋轉方法.
/// 核心旋轉方法,具體辦法是背景view旋轉,中心view和子view同角度反向旋轉,實現動畫效果
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let contentView = contentView else { return }
guard let circleView = circleView else { return }
orgin = CGPoint(x: 0.5 * ScreenInfo.Width, y: MENURADIUS + 0.17 * ScreenInfo.Height)
let currentPoint = touches.first?.location(in: self.view)
if self.touchPointInsideCircle(center: orgin!, radius: MENURADIUS*1.45, targetPoint: currentPoint!){
b = DIST(pointA: beginPoint!, pointB: orgin!)
c = DIST(pointA: currentPoint!, pointB: orgin!)
a = DIST(pointA: beginPoint!, pointB: orgin!)
let angleBegin = atan2(beginPoint!.y - orgin!.y, beginPoint!.x - orgin!.x)
let angleAfter = atan2(currentPoint!.y - orgin!.y, currentPoint!.x - orgin!.x)
let angle = angleAfter - angleBegin
// 背景view旋轉
contentView.transform = contentView.transform.rotated(by: angle)
// 中心view反向旋轉
circleView.transform = circleView.transform.rotated(by: -angle)
for i in 0..<viewArray.count {
// 子view反向旋轉
viewArray[i].transform = viewArray[i].transform.rotated(by: -angle)
// 判斷實現最下方的子view變大
let rect = viewArray[i].convert(viewArray[i].bounds, to: UIApplication.shared.keyWindow)
let viewCenterX = rect.origin.x + (rect.width) / 2
if viewCenterX > self.view.center.x - 20 && viewCenterX < self.view.center.x + 20 && rect.origin.y > contentView.center.y {
if viewArray[i].tag == 0{
viewArray[i].transform = viewArray[i].transform.scaledBy(x: 1.5, y: 1.5)
viewArray[i].tag = 1
contentView.bringSubview(toFront: viewArray[i])
}
}
else {
if viewArray[i].tag == 1 {
viewArray[i].transform = viewArray[i].transform.scaledBy(x: 1/1.5, y: 1/1.5)
viewArray[i].tag = 0
contentView.sendSubview(toBack: viewArray[i])
}
}
}
beginPoint = currentPoint
}
}
4.互動優化.
/// 獲取手指觸控位置,超過範圍不讓旋轉互動
private func touchPointInsideCircle(center: CGPoint, radius: CGFloat, targetPoint: CGPoint) -> Bool{
let dist = DIST(pointA: targetPoint, pointB: center)
return (dist <= radius)
}
func DIST(pointA: CGPoint, pointB: CGPoint) -> CGFloat{
let x = (pointA.x - pointB.x) * (pointA.x - pointB.x)
let y = (pointA.y - pointB.y) * (pointA.y - pointB.y)
return CGFloat(sqrtf(Float(x + y)))
}
demo地址:CircleView
使用pop框架實現,長按中心按鈕,周圍子View旋轉效果的專案:Swift.輪轉動畫+Pop框架
有問題歡迎探討.