如何解决自定义形状到 UISlider 并在 Swift 5 中更新进度
我已经检查了一些关于自定义 UIView 滑块的 StackOverflow 答案,但使用它们我无法制作这样的滑块。这构成了一个圆或半圆。我已经找到了一些使用 UIView 制作圆形滑块的库,但它对我没有帮助,所以任何人都可以帮助我。我怎样才能像下面的 UIImage 一样制作滑块?谢谢!
解决方法
你可能会自己动手。 (您显然可以搜索第三方实现,但这超出了 StackOverflow 的范围。)有很多方法可以解决这个问题,但这里的基本要素是:
- 整个路径的粉红色弧线。就我个人而言,我会为此使用
export default function NewEventScreen() { const [date,setDate] = useState(new Date()); const [mode,setMode] = useState('date'); const [show,setShow] = useState(false); const onChange = (event,selectedDate) => { const currentDate = selectedDate || date; setShow(Platform.OS === 'ios'); setDate(currentDate); }; const showMode = (currentMode) => { setShow(true); setMode(currentMode); }; const showDatepicker = () => { showMode('date'); }; const showTimepicker = () => { showMode('time'); }; handleEventCreation = () => { const { title,description } = this.state firebase.firestore() .collection('users').doc(firebase.auth().currentUser.uid).collection('events').doc().set({ title: title,description: description,}).then(() => { console.log("Document written") this.setState({ title: '',description: '',}) }).catch(error => console.log(error)) } state = { title: '',checked: false,} onPress = () => { this.setState({checked: !this.state.checked}) } return ( <View style={styles.container}> <Text style={styles.title}>Here can you make your events!</Text> <TextInput style={styles.inputBox} value={state.title} onChangeText={title => this.setState({ title })} placeholder='Title' /> <TextInput style={styles.inputBox} value={state.description} onChangeText={description => this.setState({ description })} placeholder='Description' /> <CheckBox center title='Click Here' checked={this.state.checked} onPress={() => this.onPress()} /> <View> <View> <Button onPress={showDatepicker} title="Show date picker!" /> </View> <View> <Button onPress={showTimepicker} title="Show time picker!" /> </View> {show && ( <DateTimePicker testID="dateTimePicker" value={date} mode={mode} is24Hour={true} display="default" onChange={onChange} /> )} </View> <TouchableOpacity style={styles.button} onPress={handleEventCreation}> <Text style={styles.buttonText}>Create Event</Text> </TouchableOpacity> </View> ) }
。 - 从开始到当前进度的白色弧线(从 0 到 1)。同样,
CAShapeLayer
是合乎逻辑的。 - 放置在当前进度点的白点。下面我创建了一个带有白色背景的
CAShapeLayer
,然后应用一个CALayer
作为它的蒙版。您也可以为此创建一个CAGradientLayer
。 - 关于如何设置进度,你将粉色和白色弧线的路径设置为相同的路径,只是更新白色弧线的
UIImage
。您还可以相应地调整白点图层的位置。 - 这里唯一复杂的是找出弧的中心。在下面的示例中,我根据视图的边界使用一些三角函数来计算它,以便弧从左下角到顶部,然后从右下角返回。或者,您也可以将圆弧的中心作为参数传递。
无论如何,它可能看起来像:
strokeEnd
上面,我设置了 @IBDesignable
class ArcView: UIView {
@IBInspectable
var lineWidth: CGFloat = 7 { didSet { updatePaths() } }
@IBInspectable
var progress: CGFloat = 0 { didSet { updatePaths() } }
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
progress = 0.35
}
lazy var currentPositionDotLayer: CALayer = {
let layer = CALayer()
layer.backgroundColor = UIColor.white.cgColor
let rect = CGRect(x: 0,y: 0,width: lineWidth * 3,height: lineWidth * 3)
layer.frame = rect
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor.white.cgColor,UIColor.clear.cgColor]
gradientLayer.type = .radial
gradientLayer.frame = rect
gradientLayer.startPoint = CGPoint(x: 0.5,y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1,y: 1)
gradientLayer.locations = [0.5,1]
layer.mask = gradientLayer
return layer
}()
lazy var progressShapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.lineCap = .round
shapeLayer.lineWidth = lineWidth
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = #colorLiteral(red: 1,green: 1,blue: 1,alpha: 1)
return shapeLayer
}()
lazy var totalShapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.lineCap = .round
shapeLayer.lineWidth = lineWidth
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = #colorLiteral(red: 0.9439327121,green: 0.5454334617,blue: 0.6426400542,alpha: 1)
return shapeLayer
}()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
override func layoutSubviews() {
super.layoutSubviews()
updatePaths()
}
}
// MARK: - Private utility methods
private extension ArcView {
func configure() {
layer.addSublayer(totalShapeLayer)
layer.addSublayer(progressShapeLayer)
layer.addSublayer(currentPositionDotLayer)
}
func updatePaths() {
let rect = bounds.insetBy(dx: lineWidth / 2,dy: lineWidth / 2)
let halfWidth = rect.width / 2
let height = rect.height
let theta = atan(halfWidth / height)
let radius = sqrt(halfWidth * halfWidth + height * height) / 2 / cos(theta)
let center = CGPoint(x: rect.midX,y: rect.minY + radius)
let delta = (.pi / 2 - theta) * 2
let startAngle = .pi * 3 / 2 - delta
let endAngle = .pi * 3 / 2 + delta
let path = UIBezierPath(arcCenter: center,radius: radius,startAngle: startAngle,endAngle: endAngle,clockwise: true)
progressShapeLayer.path = path.cgPath // white arc
totalShapeLayer.path = path.cgPath // pink arc
progressShapeLayer.strokeEnd = progress
let currentAngle = (endAngle - startAngle) * progress + startAngle
let dotCenter = CGPoint(x: center.x + radius * cos(currentAngle),y: center.y + radius * sin(currentAngle))
currentPositionDotLayer.position = dotCenter
}
}
的背景颜色,以便您可以看到它的 ArcView
,但您显然会将背景颜色设置为透明。
现在您可以添加大量附加功能(例如,添加用户交互以便“清理”它等)。例如,参见 https://github.com/robertmryan/ArcView。但设计这类东西的关键是将其分解为多个组成元素,将一个元素叠加在另一个元素之上。
您可以以编程方式设置 bounds
的 progress
以使其在 0 和 1 的值之间更改当前值:
arcView
结果:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。