被修改
与Nathan一起查看最新项目的评论部分。只剩下问题:获得正确的按钮。
被修改
我想要一个用户可以旋转的UIView。 UIView应包含一些可以单击的UIButton。我很难,因为我使用UIControl子类来制作旋转视图,在该子类中,我必须禁用UIControl中子视图的用户交互(使其旋转),这可能导致UIButton无法点击。如何创建用户可以旋转和包含可点击的UIButtons的UIView? This是我项目的链接,它为您提供了一个良好的开端:它包含UIButtons和可旋转的UIView。但是我可以不点击UIButtons。
包含更多详情的旧问题
我正在使用这个pod:https://github.com/joshdhenry/SpinWheelControl,我想对按钮点击做出反应。我可以添加按钮,但是我无法在按钮中接收点击事件。我正在使用hitTests,但它们永远不会被执行。用户应旋转滚轮并能够单击其中一个饼图中的按钮。
在此处获取项目:https://github.com/Jasperav/SpinningWheelWithTappableButtons
请参阅我在pod文件中添加的代码:
我在SpinWheelWedge.swift中添加了这个变量:
let button = SpinWheelWedgeButton()
我添加了这个课程:
class SpinWheelWedgeButton: TornadoButton {
public func configureWedgeButton(index: UInt, width: CGFloat, position: CGPoint, radiansPerWedge: Radians) {
self.frame = CGRect(x: 0, y: 0, width: width, height: 30)
self.layer.anchorPoint = CGPoint(x: 1.1, y: 0.5)
self.layer.position = position
self.transform = CGAffineTransform(rotationAngle: radiansPerWedge * CGFloat(index) + CGFloat.pi + (radiansPerWedge / 2))
self.backgroundColor = .green
self.addTarget(self, action: #selector(pressed(_:)), for: .touchUpInside)
}
@IBAction func pressed(_ sender: TornadoButton){
print("hi")
}
}
这是TornadoButton类:
class TornadoButton: UIButton{
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let pres = self.layer.presentation()!
let suppt = self.convert(point, to: self.superview!)
let prespt = self.superview!.layer.convert(suppt, to: pres)
if (pres.hitTest(suppt)) != nil{
return self
}
return super.hitTest(prespt, with: event)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let pres = self.layer.presentation()!
let suppt = self.convert(point, to: self.superview!)
return (pres.hitTest(suppt)) != nil
}
}
我将此添加到SpinWheelControl.swift中,在循环中#34; for wedgeNumber in"
wedge.button.configureWedgeButton(index: wedgeNumber, width: radius * 2, position: spinWheelCenter, radiansPerWedge: radiansPerWedge)
wedge.addSubview(wedge.button)
这是我认为我可以在SpinWheelControl.swift中找回按钮的地方:
override open func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
let p = touch.location(in: touch.view)
let v = touch.view?.hitTest(p, with: nil)
print(v)
}
只有' v'永远是旋转轮本身,永远不是按钮。我也没有看到按钮打印,并且从未执行过hittest。这段代码出了什么问题,为什么hitTest没有执行?我宁愿拥有正常的UIBUtton,但我认为我需要进行测试。
答案 0 :(得分:4)
我能够对项目进行修改,我认为我能解决您的问题。
SpinWheelControl
课程中,您要将userInteractionEnabled
的{{1}}属性设置为spinWheelView
。请注意,这不是您想要的,因为您仍然有兴趣点击false
内的按钮。但是,如果你没有关闭用户互动,那么轮子就不会转动,因为孩子们的观点搞得一团糟!spinWheelView
。touchUpInside
的{{1}}方法。调用endTracking
方法后,我们会手动遍历所有按钮并为其调用SpinWheelControl
。endTracking
。解决方法是覆盖按钮的endTracking
方法,并仅在该特定按钮的触摸endTracking
为真时手动触发endTracking
方法。<强>代码:强>
TornadoButton 类:(不再需要自定义.touchUpInside
和hitTest
,因为我们不再对通常的点击测试感兴趣;我们只是直接调用{{ 1}})
hitTest
SpinWheelControl 类:pointInside
方法:
endTracking
另外,要测试是否正在调用右键,只需在创建时将按钮的标记设置为class TornadoButton: UIButton{
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
if let t = touch {
if self.hitTest(t.location(in: self), with: event) != nil {
print("Tornado button with tag \(self.tag) ended tracking")
self.sendActions(for: [.touchUpInside])
}
}
}
}
即可。使用此方法,您不需要像@nathan那样使用自定义偏移量,因为右侧按钮将响应endTracking
,您可以通过override open func endTracking(_ touch: UITouch?, with event: UIEvent?) {
for sv in self.spinWheelView.subviews {
if let wedge = sv as? SpinWheelWedge {
wedge.button.endTracking(touch, with: event)
}
}
...
}
获取其标记。
答案 1 :(得分:3)
以下是针对特定项目的解决方案:
在drawWheel
的{{1}}功能中,在SpinWheelControl.swift
上启用用户互动。为此,请删除以下行:
spinWheelView
再次在self.spinWheelView.isUserInteractionEnabled = false
函数中,将按钮设为drawWheel
的子视图,而不是spinWheelView
。在楔形后添加按钮作为子视图,因此它将出现在楔形图层的顶部。
旧:
wedge
新:
wedge.button.configureWedgeButton(index: wedgeNumber, width: radius * 0.45, position: spinWheelCenter, radiansPerWedge: radiansPerWedge)
wedge.addSubview(wedge.button)
spinWheelView.addSubview(wedge)
创建一个新的wedge.button.configureWedgeButton(index: wedgeNumber, width: radius * 0.45, position: spinWheelCenter, radiansPerWedge: radiansPerWedge)
spinWheelView.addSubview(wedge)
spinWheelView.addSubview(wedge.button)
子类,将触摸传递到其子视图。
UIView
在class PassThroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for subview in subviews {
if !subview.isHidden && subview.alpha > 0 && subview.isUserInteractionEnabled && subview.point(inside: convert(point, to: subview), with: event) {
return true
}
}
return false
}
}
函数的最开始,声明drawWheel
为spinWheelView
类型。这将允许按钮接收触摸事件。
PassThroughView
通过这些改动,您应该得到以下行为:
(按下任何按钮时,信息将打印到控制台。)
此解决方案允许用户像往常一样旋转滚轮,以及点击任何按钮。但是,这可能不是满足您需求的完美解决方案,因为存在一些限制:
根据您的需要,您可以考虑构建自己的微调器,而不是依赖第三方pod。这个pod的难点在于它使用spinWheelView = PassThroughView(frame: self.bounds)
和相关函数而不是手势识别器。如果您使用了手势识别器,则可以更轻松地使用所有beginTracking(_ touch: UITouch, with event: UIEvent?)
功能。
或者,如果您只想识别楔形范围内的降落事件,则可以进一步追求UIButton
想法。
编辑:确定按下了哪个按钮。
如果我们知道方向盘的hitTest
和起始selectedIndex
,我们就可以计算出按下了哪个按钮。
目前,起始selectedIndex
为0,按钮标签顺时针增加。点击所选按钮(标签= 0),打印7,这意味着按钮是&#34;旋转&#34;处于起始状态的7个位置。如果车轮在不同位置开始,则该值会有所不同。
这是一个快速功能,用于确定使用两条信息点按的按钮标记:来自当前selectedIndex
的方向盘selectedIndex
和subview.tag
实施point(inside point: CGPoint, with event: UIEvent?)
。
PassThroughView
同样,这绝对是一个黑客攻击,但它确实有效。如果您打算继续为此微调器控件添加功能,我强烈建议您创建自己的控件,以便您可以从头开始设计它以满足您的需求。
答案 2 :(得分:1)
一般解决方案是使用public class FibonacciSequence {
/**
* @param args
*/
public int[] fibonacci(int numArray[]) {
for (int i = 1; i < numArray.length; i++) {
System.out.println(numArray[i] + numArray[i - 1]);
}
return numArray;
}
public static void main(String[] args) {
int a[]=new int []{1,2,3,4,5};
System.out.println(""+Arrays.toString(new FibonacciSequence().fibonacci(a)));
}
}
并将所有UIView
放置在应有的位置,然后使用UIButton
旋转视图,计算速度和方向向量旋转你的视图。为了旋转您的视图,我建议使用UIPanGestureRecognizer
,因为它可以设置动画,并且您的子视图也会旋转。 (额外:如果你想将transform
的方向始终向下设置,只需将它们反向旋转,这将使它们始终向下看)
有些人还使用UIButtons
代替UIScrollView
。将描述的视图放在UIPanGestureRecognizer
内并使用UIScrollView
的委托方法计算速度和方向,然后按照描述将这些值应用于UIScrollView
。这种黑客攻击的原因是UIView
自动降低速度并提供更好的体验。 (使用此技术,您应该将UIScrollView
设置为非常大的内容,并定期将contentSize
contentOffset
重新定位到UIScrollView
。
但我强烈建议采用第一种方法。
答案 3 :(得分:1)
至于我的观点,你可以使用你自己的视图,只需要几个子层和你需要的所有其他东西。 在这种情况下,你将获得充分的灵活性,但你也应该多写一些代码。
如果您喜欢这个选项,您可以在下面的gif上获得类似的内容(您可以根据需要自定义它 - 添加文本,图像,动画等):
在这里,我向您展示2个连续平移和紫色部分上的一个水龙头 - 当检测到水龙头时6 bg颜色变为绿色
要检测我使用touchesBegan
的点按,如下所示。
要使用此代码,您可以将下面的代码复制粘贴到游乐场并根据需要进行修改
//: A UIKit based Playground for presenting user interface
import UIKit import PlaygroundSupport
class RoundView : UIView {
var sampleArcLayer:CAShapeLayer = CAShapeLayer()
func performRotation( power: Float) {
let maxDuration:Float = 2
let maxRotationCount:Float = 5
let currentDuration = maxDuration * power
let currrentRotationCount = (Double)(maxRotationCount * power)
let fromValue:Double = Double(atan2f(Float(transform.b), Float(transform.a)))
let toValue = Double.pi * currrentRotationCount + fromValue
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = fromValue
rotateAnimation.toValue = toValue
rotateAnimation.duration = CFTimeInterval(currentDuration)
rotateAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
rotateAnimation.isRemovedOnCompletion = true
layer.add(rotateAnimation, forKey: nil)
layer.transform = CATransform3DMakeRotation(CGFloat(toValue), 0, 0, 1)
}
override func layoutSubviews() {
super.layoutSubviews()
drawLayers()
}
private func drawLayers()
{
sampleArcLayer.removeFromSuperlayer()
sampleArcLayer.frame = bounds
sampleArcLayer.fillColor = UIColor.purple.cgColor
let proportion = CGFloat(20)
let centre = CGPoint (x: frame.size.width / 2, y: frame.size.height / 2)
let radius = frame.size.width / 2
let arc = CGFloat.pi * 2 * proportion / 100 // i.e. the proportion of a full circle
let startAngle:CGFloat = 45
let cPath = UIBezierPath()
cPath.move(to: centre)
cPath.addLine(to: CGPoint(x: centre.x + radius * cos(startAngle), y: centre.y + radius * sin(startAngle)))
cPath.addArc(withCenter: centre, radius: radius, startAngle: startAngle, endAngle: arc + startAngle, clockwise: true)
cPath.addLine(to: CGPoint(x: centre.x, y: centre.y))
sampleArcLayer.path = cPath.cgPath
// you can add CATExtLayer and any other stuff you need
layer.addSublayer(sampleArcLayer)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let point = touches.first?.location(in: self) {
if let layerArray = layer.sublayers {
for sublayer in layerArray {
if sublayer.contains(point) {
if sublayer == sampleArcLayer {
if sampleArcLayer.path?.contains(point) == true {
backgroundColor = UIColor.green
}
}
}
}
}
}
}
}
class MyViewController : UIViewController {
private var lastTouchPoint:CGPoint = CGPoint.zero
private var initialTouchPoint:CGPoint = CGPoint.zero
private let testView:RoundView = RoundView(frame:CGRect(x: 40, y: 40, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
testView.layer.cornerRadius = testView.frame.height / 2
testView.layer.masksToBounds = true
testView.backgroundColor = UIColor.red
view.addSubview(testView)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(MyViewController.didDetectPan(_:)))
testView.addGestureRecognizer(panGesture)
}
@objc func didDetectPan(_ gesture:UIPanGestureRecognizer) {
let touchPoint = gesture.location(in: testView)
switch gesture.state {
case .began:
initialTouchPoint = touchPoint
break
case .changed:
lastTouchPoint = touchPoint
break
case .ended, .cancelled:
let delta = initialTouchPoint.y - lastTouchPoint.y
let powerPercentage = max(abs(delta) / testView.frame.height, 1)
performActionOnView(scrollPower: Float(powerPercentage))
initialTouchPoint = CGPoint.zero
break
default:
break
}
}
private func performActionOnView(scrollPower:Float) {
testView.performRotation(power: scrollPower)
} } // Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()