如何用 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( ...
随机推荐
- C++内置类型对象之间的转换
C++定义了一组内置类型对象之间的标准转换,在必要时它们被编译器隐式地应用到对象上. 隐式类型转换发生在下列这些典型情况下. 1. 在混合类型的算数表达式中 规则:在这种情况下最宽的数据类型成为目标转 ...
- 最大流之sap算法
若有向图G = (V , E)满足下列条件: 1.有且仅有一个顶点S,它的入度为 0 ,这个顶点称为源点. 2.有且仅有一个顶点T,它的出度为 0 ,这个顶点称为汇点. 3.每一条弧都有一个非负数,叫 ...
- [LeetCode]题解(python):099-Recover Binary Search Tree
题目来源: https://leetcode.com/problems/recover-binary-search-tree/ 题意分析: 二叉搜索树中有两个点错了位置,恢复这棵树. 题目思路: 如果 ...
- [转]Swift 简介 - 苹果最新的编程语言
Swift 真的可以说是最新的编程语言了,2014wwdc刚刚发布,下面来了解一下都有哪些特点. 首先感谢原作者,主要内容是借鉴他的,参考链接 http://zh.lucida.me/blog/an- ...
- (IOS)关于Xcode的架构(Architectures)设置
首先来了解一下Architectures中几个参数的含义 ARMv6:ARM11内核用于iPhone2G和iPhone3G中的架构 ARMv7:modern ARM内核用于iPhone3GS和iPho ...
- GraphLab:新的面向机器学习的并行框架
大规模图数据计算引起了许多知名公司的关注,微软提出了用于图数据匹配的Horton - Querying Large Distributed Graphs(Link:http://research.mi ...
- SIGAR - System Information Gatherer And Reporter
https://support.hyperic.com/display/SIGAR/Home 收藏一篇: http://www.cnitblog.com/houcy/archive/2012/11/2 ...
- Jass 技能模型定义(转)
Jass是什么? 先阐释一下什么是jass吧,百度:JASS(正确地说是JASS 2)是魔兽3的程序语言,用于控制游戏和地图的进行,也是魔兽游戏和地图的基础. 地图编辑器中摆放的单位(Un ...
- perl5 第十三章 Perl的面向对象编程
第十三章 Perl的面向对象编程 by flamephoenix 一.模块简介二.Perl中的类三.创建类四.构造函数 实例变量 五.方法六.方法的输出七.方法的调用八.重载九.析构函数十.继承十一. ...
- 光学基础知识:白光、颜色混合、RGB、色彩空间
1665年,牛顿(Isaac Newton)进行了太阳光实验,让太阳光通过窗板的小圆孔照射在玻璃三角棱镜上,光束在棱镜中折射后,扩散为一个连续的彩虹颜色带,牛顿称之为光谱,表示连续的可见光谱.而可见光 ...