Effect:
Static: sub view s are arranged symmetrically, allowing dynamic addition, and 0 ~ 24 can be displayed well
When rotating: the center view does not move, the sub view rotates with the gesture, and the bottom sub view becomes larger and prominent
Implementation ideas:
All the controls are added to a large background view. In essence, the background view is rotated. When the background view is rotated, all its child controls are rotated in the opposite direction. This effect is achieved
Using touchMoved method to get gesture, using transform to achieve animation
The view at the bottom becomes larger is the loop judgment sub view.frame.x. when it is in a range and frame.y is larger than the center view.frame.y, modify its transform to make it larger, and modify its tag to mark that it has become larger. When its frame.x is out of the predetermined range, restore it
Implementation mode:
1. Add background transparent view and center circle view
2. Add peripheral rotor view
3. Add rotation method
4. Interactive optimization
1. Add background transparent view and center circle view
///Add background view, which is also a rotated 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!) } ///Add middle circle 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)) ///In order to adapt and ensure the size change center unchanged 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. Add peripheral rotor view
///Sub view of layout rotation private func rotationCircleCenter(contentOrgin: CGPoint, contentRadius: CGFloat,subnode: [String]){ // Add proportion to realize adaptive size when the number of child view s to be added is large 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))) // When the number of child views is greater than 10, the view.size becomes smaller to prevent view offset, and the view.center should be kept unchanged 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() // This tag determines whether the view is in the state of becoming larger at the bottom, and in the state of non becoming larger 0, it becomes larger 1 view.tag = 0 // Get the rect of the child view in the current screen to make the bottom one bigger 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. Add rotation method
///The core rotation method, the specific method is the background view rotation, the center view and the sub view rotate at the same angle in reverse, to achieve the animation effect 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 // Background view rotation contentView.transform = contentView.transform.rotated(by: angle) // Center view reverse rotation circleView.transform = circleView.transform.rotated(by: -angle) for i in 0..<viewArray.count { // Child view reverse rotation viewArray[i].transform = viewArray[i].transform.rotated(by: -angle) // Judge whether the child view at the bottom of the implementation is larger 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. Interactive optimization
///Get the finger touch position, beyond the range, do not allow rotation interaction 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 address: CircleView
Using pop framework to achieve, long press the center button, circle the sub View rotation effect items: Swift. Rotation animation + Pop frame
If you have any questions, welcome to discuss