如何用 Swift 语言构建一个自定控件


- import UIKit
- class ViewController: UIViewController {
- let rangeSlider = RangeSlider(frame: CGRectZero)
- override func viewDidLoad() {
- super.viewDidLoad()
- rangeSlider.backgroundColor = UIColor.redColor()
- view.addSubview(rangeSlider)
- }
- override func viewDidLayoutSubviews() {
- let margin: CGFloat = 20.0
- let width = view.bounds.width - 2.0 * margin
- rangeSlider.frame = CGRect(x: margin, y: margin + topLayoutGuide.length,
- width: width, height: 31.0)
- }
- }

- import UIKit
- class RangeSlider: UIControl {
- var minimumValue = 0.0
- var maximumValue = 1.0
- var lowerValue = 0.2
- var upperValue = 0.8
- }
- import QuartzCore
- let trackLayer = CALayer()
- let lowerThumbLayer = CALayer()
- let upperThumbLayer = CALayer()
- var thumbWidth: CGFloat {
- return CGFloat(bounds.height)
- }
- override init(frame: CGRect) {
- super.init(frame: frame)
- trackLayer.backgroundColor = UIColor.blueColor().CGColor
- layer.addSublayer(trackLayer)
- lowerThumbLayer.backgroundColor = UIColor.greenColor().CGColor
- layer.addSublayer(lowerThumbLayer)
- upperThumbLayer.backgroundColor = UIColor.greenColor().CGColor
- layer.addSublayer(upperThumbLayer)
- updateLayerFrames()
- }
- required init(coder: NSCoder) {
- super.init(coder: coder)
- }
- func updateLayerFrames() {
- trackLayer.frame = bounds.rectByInsetting(dx: 0.0, dy: bounds.height / 3)
- trackLayer.setNeedsDisplay()
- let lowerThumbCenter = CGFloat(positionForValue(lowerValue))
- lowerThumbLayer.frame = CGRect(x: lowerThumbCenter - thumbWidth / 2.0, y: 0.0,
- width: thumbWidth, height: thumbWidth)
- lowerThumbLayer.setNeedsDisplay()
- let upperThumbCenter = CGFloat(positionForValue(upperValue))
- upperThumbLayer.frame = CGRect(x: upperThumbCenter - thumbWidth / 2.0, y: 0.0,
- width: thumbWidth, height: thumbWidth)
- upperThumbLayer.setNeedsDisplay()
- }
- func positionForValue(value: Double) -> Double {
- let widthDouble = Double(thumbWidth)
- return Double(bounds.width - thumbWidth) * (value - minimumValue) /
- (maximumValue - minimumValue) + Double(thumbWidth / 2.0)
- }
- override var frame: CGRect {
- didSet {
- updateLayerFrames()
- }
- }

- import UIKit
- import QuartzCore
- class RangeSliderThumbLayer: CALayer {
- var highlighted = false
- weak var rangeSlider: RangeSlider?
- }
- let lowerThumbLayer = RangeSliderThumbLayer()
- let upperThumbLayer = RangeSliderThumbLayer()
- lowerThumbLayer.rangeSlider = self
- upperThumbLayer.rangeSlider = self
- var previousLocation = CGPoint()
- override func beginTrackingWithTouch(touch: UITouch!, withEvent event: UIEvent!) -> Bool {
- previousLocation = touch.locationInView(self)
- // Hit test the thumb layers
- if lowerThumbLayer.frame.contains(previousLocation) {
- lowerThumbLayer.highlighted = true
- } else if upperThumbLayer.frame.contains(previousLocation) {
- upperThumbLayer.highlighted = true
- }
- return lowerThumbLayer.highlighted || upperThumbLayer.highlighted
- }
- func boundValue(value: Double, toLowerValue lowerValue: Double, upperValue: Double) -> Double {
- return min(max(value, lowerValue), upperValue)
- }
- override func continueTrackingWithTouch(touch: UITouch!, withEvent event: UIEvent!) -> Bool {
- let location = touch.locationInView(self)
- // 1. Determine by how much the user has dragged
- let deltaLocation = Double(location.x - previousLocation.x)
- let deltaValue = (maximumValue - minimumValue) * deltaLocation / Double(bounds.width - bounds.height)
- previousLocation = location
- // 2. Update the values
- if lowerThumbLayer.highlighted {
- lowerValue += deltaValue
- lowerValue = boundValue(lowerValue, toLowerValue: minimumValue, upperValue: upperValue)
- } else if upperThumbLayer.highlighted {
- upperValue += deltaValue
- upperValue = boundValue(upperValue, toLowerValue: lowerValue, upperValue: maximumValue)
- }
- // 3. Update the UI
- CATransaction.begin()
- CATransaction.setDisableActions(true)
- updateLayerFrames()
- CATransaction.commit()
- return true
- }
- override func endTrackingWithTouch(touch: UITouch!, withEvent event: UIEvent!) {
- lowerThumbLayer.highlighted = false
- upperThumbLayer.highlighted = false
- }

- sendActionsForControlEvents(.ValueChanged)
- rangeSlider.addTarget(self, action: "rangeSliderValueChanged:", forControlEvents: .ValueChanged)
- func rangeSliderValueChanged(rangeSlider: RangeSlider) {
- println("Range slider value changed: (\(rangeSlider.lowerValue) \(rangeSlider.upperValue))")
- }
- Range slider value changed: (0.117670682730924 0.390361445783134)
- Range slider value changed: (0.117670682730924 0.38835341365462)
- Range slider value changed: (0.117670682730924 0.382329317269078)
- Range slider value changed: (0.117670682730924 0.380321285140564)
- Range slider value changed: (0.119678714859438 0.380321285140564)
- Range slider value changed: (0.121686746987952 0.380321285140564)
- import UIKit
- import QuartzCore
- class RangeSliderTrackLayer: CALayer {
- weak var rangeSlider: RangeSlider?
- }
- let trackLayer = RangeSliderTrackLayer()
- init(frame: CGRect) {
- super.init(frame: frame)
- trackLayer.rangeSlider = self
- trackLayer.contentsScale = UIScreen.mainScreen().scale
- layer.addSublayer(trackLayer)
- lowerThumbLayer.rangeSlider = self
- lowerThumbLayer.contentsScale = UIScreen.mainScreen().scale
- layer.addSublayer(lowerThumbLayer)
- upperThumbLayer.rangeSlider = self
- upperThumbLayer.contentsScale = UIScreen.mainScreen().scale
- layer.addSublayer(upperThumbLayer)
- }
- rangeSlider.backgroundColor = UIColor.redColor()

- var trackTintColor = UIColor(white: 0.9, alpha: 1.0)
- var trackHighlightTintColor = UIColor(red: 0.0, green: 0.45, blue: 0.94, alpha: 1.0)
- var thumbTintColor = UIColor.whiteColor()
- var curvaceousness : CGFloat = 1.0
- override func drawInContext(ctx: CGContext!) {
- if let slider = rangeSlider {
- // Clip
- let cornerRadius = bounds.height * slider.curvaceousness / 2.0
- let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
- CGContextAddPath(ctx, path.CGPath)
- // Fill the track
- CGContextSetFillColorWithColor(ctx, slider.trackTintColor.CGColor)
- CGContextAddPath(ctx, path.CGPath)
- CGContextFillPath(ctx)
- // Fill the highlighted range
- CGContextSetFillColorWithColor(ctx, slider.trackHighlightTintColor.CGColor)
- let lowerValuePosition = CGFloat(slider.positionForValue(slider.lowerValue))
- let upperValuePosition = CGFloat(slider.positionForValue(slider.upperValue))
- let rect = CGRect(x: lowerValuePosition, y: 0.0, width: upperValuePosition - lowerValuePosition, height: bounds.height)
- CGContextFillRect(ctx, rect)
- }
- }

- override func drawInContext(ctx: CGContext!) {
- if let slider = rangeSlider {
- let thumbFrame = bounds.rectByInsetting(dx: 2.0, dy: 2.0)
- let cornerRadius = thumbFrame.height * slider.curvaceousness / 2.0
- let thumbPath = UIBezierPath(roundedRect: thumbFrame, cornerRadius: cornerRadius)
- // Fill - with a subtle shadow
- let shadowColor = UIColor.grayColor()
- CGContextSetShadowWithColor(ctx, CGSize(width: 0.0, height: 1.0), 1.0, shadowColor.CGColor)
- CGContextSetFillColorWithColor(ctx, slider.thumbTintColor.CGColor)
- CGContextAddPath(ctx, thumbPath.CGPath)
- CGContextFillPath(ctx)
- // Outline
- CGContextSetStrokeColorWithColor(ctx, shadowColor.CGColor)
- CGContextSetLineWidth(ctx, 0.5)
- CGContextAddPath(ctx, thumbPath.CGPath)
- CGContextStrokePath(ctx)
- if highlighted {
- CGContextSetFillColorWithColor(ctx, UIColor(white: 0.0, alpha: 0.1).CGColor)
- CGContextAddPath(ctx, thumbPath.CGPath)
- CGContextFillPath(ctx)
- }
- }
- }
- var highlighted: Bool = false {
- didSet {
- setNeedsDisplay()
- }
- }

- var minimumValue: Double = 0.0 {
- didSet {
- updateLayerFrames()
- }
- }
- var maximumValue: Double = 1.0 {
- didSet {
- updateLayerFrames()
- }
- }
- var lowerValue: Double = 0.2 {
- didSet {
- updateLayerFrames()
- }
- }
- var upperValue: Double = 0.8 {
- didSet {
- updateLayerFrames()
- }
- }
- var trackTintColor: UIColor = UIColor(white: 0.9, alpha: 1.0) {
- didSet {
- trackLayer.setNeedsDisplay()
- }
- }
- var trackHighlightTintColor: UIColor = UIColor(red: 0.0, green: 0.45, blue: 0.94, alpha: 1.0) {
- didSet {
- trackLayer.setNeedsDisplay()
- }
- }
- var thumbTintColor: UIColor = UIColor.whiteColor() {
- didSet {
- lowerThumbLayer.setNeedsDisplay()
- upperThumbLayer.setNeedsDisplay()
- }
- }
- var curvaceousness: CGFloat = 1.0 {
- didSet {
- trackLayer.setNeedsDisplay()
- lowerThumbLayer.setNeedsDisplay()
- upperThumbLayer.setNeedsDisplay()
- }
- }
- CATransaction.begin()
- CATransaction.setDisableActions(true)
- CATransaction.commit()
- // 3. Update the UI
- CATransaction.begin()
- CATransaction.setDisableActions(true)
- updateLayerFrames()
- CATransaction.commit()
- let time = dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_SEC))
- dispatch_after(time, dispatch_get_main_queue()) {
- self.rangeSlider.trackHighlightTintColor = UIColor.redColor()
- self.rangeSlider.curvaceousness = 0.0
- }


如何用 Swift 语言构建一个自定控件的更多相关文章
- 前端每日实战:158# 视频演示如何用纯 CSS 创作一个雨伞 toggle 控件
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/pxLbjv 可交互视频 此视频是可 ...
- 前端每日实战:145# 视频演示如何用纯 CSS 创作一个电源开关控件
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/PdMyJd 可交互视频 此视频是可 ...
- Pro Android 4 第六章 构建用户界面以及使用控件(一)
目前为止,我们已经介绍了android的基础内容,但是还没开始接触用户界面(UI).本章我们将开始探讨用户界面和控件.我们先讨论一下android中UI设计的一般原理,然后我们在介绍一下an ...
- WPF系列 自定控件
引言 WPF中微软提供了一些基本的控件,但是工作中这些基础的控件往往不能满足我们的需求,这个时候我们就需要根据实际的需求去开发自己的控件,但要注意不是所有功能不满足的情况都需要通过自定义控件来实现.实 ...
- Qt 创建一个QtDesinger第三方控件
1.需要创建一个合适的.pro文件 2.创建一个继承QDesignerCustomWidgetInterface的类,描述控件的一些属性. 函数 描述和返回值 name() 指定控件的名称 group ...
- 【React】开发一个城市选择控件
想到做这个,是因为无意中在github上看到了这一个仓库https://github.com/lunlunshiwo/ChooseCity,做的就是一个城市选择控件,是用vue写的,说的是阿里的一道题 ...
- android中一个评分的控件
RatingBar android中一个评分的控件 如何使用 Android Studio下: dependencies { compile 'com.hedgehog.ratingbar:app:1 ...
- 【WPF学习】第六十四章 构建基本的用户控件
创建一个简单用户控件是开始自定义控件的好方法.本章主要介绍创建一个基本的颜色拾取器.接下来分析如何将这个控件分解成功能更强大的基于模板的控件. 创建基本的颜色拾取器很容易.然而,创建自定义颜色拾取器仍 ...
- C++ 一个程序获取另一个程序Edit控件的内容
//一个程序获取另一个程序Edit控件的内容 //根据指定程序的标题名获取改程序窗口的句柄 HWND hWnd=::FindWindow(NULL,"zhang001"); if( ...
随机推荐
- Undefined symbols for architecture xxx
解决方法: "Build Settings"->"Linking"->"Other Linker Flags" add the ...
- X509Certificate2 本地正常,放到线上内部错误
iis 找到部署的站点应用连接池,右键高级设置,找到“加载用户配置文件”改为true.window service2008 默认为false的.
- php制作数据字典
/** * 生成mysql数据字典 */ header("Content-type:text/html;charset=utf-8"); // 配置数据库 $database = ...
- python初探-collections容器数据类型
collections容器数据类型是对基本数据类型的补充,简单介绍下计数器.有序字典.默认字典.可命名元祖.队列. 计数器(Counter) Counter是对字典类型的补充,用于追踪值得出现次数 c ...
- Windows10笔记本双显卡导致的启动黑屏解决办法之一
参考链接:http://www.zhihu.com/question/33662311 大概就是关掉ulps. ulps,显卡的多核心超低功率状态,节能用的,AMD出的双显卡的一种节能方案.不过,与某 ...
- invalid nib registered for identifier (重用符) - nib must contain exactly one top level object which must be a UITableViewCell instance'
通过xib创建cell的时候 一定要注意!!! 这个错误是在这个xib中在View同一层级出现了其他的控件,检查一下xib中左边的层级关系,让cell的view是唯一的控件就可以了,否则一执行 就会提 ...
- Android手机之间通过声音传输信息方法——声波通信(含project代码)
大家可能都用过支付宝的声波支付.即两个终端同一时候打开该功能,一个终端作为发送端send,一个终端作为接收端get,send将本终端上发出的请求信息依照约定规则的算法进行声音编码,并播放出来,get端 ...
- js推断指定函数、变量是否存在的方法
//推断是否存在指定函数 function isExitsFunction(funcName) { try { if (typeof (eval(funcName)) == "functio ...
- Android Activity和Intent机制学习笔记
转自 http://www.cnblogs.com/feisky: Activity Android中,Activity是所有程序的根本,所有程序的流程都运行在Activity之中,Activity具 ...
- Hadoop平台安装前准备
集群配置 准备工作 1. Iptables #chkconfig iptables –list #chkconfig iptables –level 3456off #service iptable ...