diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97c11c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +Carthage/ \ No newline at end of file diff --git a/Classes/TKRubberPageControl.swift b/Classes/TKRubberPageControl.swift index 35ef07b..bdd2b84 100755 --- a/Classes/TKRubberPageControl.swift +++ b/Classes/TKRubberPageControl.swift @@ -9,27 +9,12 @@ import UIKit -// ValueChange -typealias UIControlValueChangeClosure = (Any) -> Void - -// 快速获得 W 和 H -extension UIView{ - var w : CGFloat{ - return self.bounds.width - } - var h : CGFloat{ - return self.bounds.height - } -} - - - // MARK: - MoveDorection // 运动方向 -enum TKMoveDirection{ +private enum TKMoveDirection{ case left case right func toBool() -> Bool{ @@ -42,39 +27,30 @@ enum TKMoveDirection{ } } + // MARK: - TKRubberPageControlConfig // 样式配置 (含默认配置) -struct TKRubberPageControlConfig { - // 小球尺寸 - var smallBubbleSize : CGFloat = 16 - // 大球尺寸 - var mainBubbleSize : CGFloat = 40 - // 小球间距 - var bubbleXOffsetSpace : CGFloat = 12 - // 纵向间距 - var bubbleYOffsetSpace : CGFloat = 8 - // 动画时长 - var animationDuration : CFTimeInterval = 0.2 - // 小球运动半径 - var smallBubbleMoveRadius : CGFloat {return smallBubbleSize + bubbleXOffsetSpace} - - // 横条背景颜色 - var backgroundColor : UIColor = UIColor(red: 0.357, green: 0.196, blue: 0.337, alpha: 1) - // 小球颜色 - var smallBubbleColor : UIColor = UIColor(red: 0.961, green: 0.561, blue: 0.518, alpha: 1) - // 大球颜色 - var bigBubbleColor : UIColor = UIColor(red: 0.788, green: 0.216, blue: 0.337, alpha: 1) +public struct TKRubberPageControlConfig { + var smallBubbleSize: CGFloat = 16 // 小球尺寸 + var mainBubbleSize: CGFloat = 40 // 大球尺寸 + var bubbleXOffsetSpace: CGFloat = 12 // 小球间距 + var bubbleYOffsetSpace: CGFloat = 8 // 纵向间距 + var animationDuration: CFTimeInterval = 0.2 // 动画时长 + var smallBubbleMoveRadius: CGFloat {return smallBubbleSize + bubbleXOffsetSpace} // 小球运动半径 + var backgroundColor: UIColor = UIColor(red: 0.357, green: 0.196, blue: 0.337, alpha: 1) // 横条背景颜色 + var smallBubbleColor: UIColor = UIColor(red: 0.961, green: 0.561, blue: 0.518, alpha: 1) // 小球颜色 + var bigBubbleColor: UIColor = UIColor(red: 0.788, green: 0.216, blue: 0.337, alpha: 1) // 大球颜色 } // MARK: PageControl -class TKRubberPageControl : UIControl { +public class TKRubberPageControl : UIControl { // 页数 - var numberOfpage : Int = 5{ + public var numberOfpage : Int = 5{ didSet{ if oldValue != numberOfpage{ resetRubberIndicator() @@ -83,41 +59,46 @@ class TKRubberPageControl : UIControl { } // 当前 Index - var currentIndex = 0 - + public var currentIndex = 0 { + didSet { + changIndexToValue(currentIndex) + } + } // 事件闭包 - var valueChange : UIControlValueChangeClosure? + public var valueChange : ((Int) -> Void)? // 样式配置 - var styleConfig : TKRubberPageControlConfig! + public var styleConfig : TKRubberPageControlConfig { + didSet { + resetRubberIndicator() + } + } //手势 - var indexTap : UITapGestureRecognizer! - - + private var indexTap : UITapGestureRecognizer? // 所有图层 - var smallBubbles = Array() - var backgroundLayer = CAShapeLayer() - var mainBubble = CAShapeLayer() - var backLineLayer = CAShapeLayer() + private var smallBubbles = [TKBubbleCell]() + private var backgroundLayer = CAShapeLayer() + private var mainBubble = CAShapeLayer() + private var backLineLayer = CAShapeLayer() // 大球缩放比例 - let bubbleScale : CGFloat = 1/3.0 + private let bubbleScale : CGFloat = 1/3.0 // 存储计算用的 - var xPointbegin : CGFloat = 0 - var xPointEnd : CGFloat = 0 - var yPointbegin : CGFloat = 0 - var yPointEnd : CGFloat = 0 + private var xPointbegin : CGFloat = 0 + private var xPointEnd : CGFloat = 0 + private var yPointbegin : CGFloat = 0 + private var yPointEnd : CGFloat = 0 - init(frame: CGRect, count: Int, config: TKRubberPageControlConfig = TKRubberPageControlConfig()) { + public init(frame: CGRect, count: Int, config: TKRubberPageControlConfig = TKRubberPageControlConfig()) { numberOfpage = count styleConfig = config super.init(frame: frame) self.setUpView() } - required init?(coder aDecoder: NSCoder) { + public required init?(coder aDecoder: NSCoder) { styleConfig = TKRubberPageControlConfig() super.init(coder: aDecoder) self.setUpView() @@ -127,10 +108,13 @@ class TKRubberPageControl : UIControl { // 一些奇怪的位置计算 - let y = (self.h - (styleConfig.smallBubbleSize + 2 * styleConfig.bubbleYOffsetSpace))/2 + let y = (bounds.height - (styleConfig.smallBubbleSize + 2 * styleConfig.bubbleYOffsetSpace))/2 let w = CGFloat(numberOfpage - 2) * styleConfig.smallBubbleSize + styleConfig.mainBubbleSize + CGFloat(numberOfpage) * styleConfig.bubbleXOffsetSpace let h = styleConfig.smallBubbleSize + styleConfig.bubbleYOffsetSpace * 2 - let x = (self.w - w)/2 + let x = (bounds.width - w)/2 + if w > bounds.width || h > bounds.height { + print("Draw UI control out off rect") + } xPointbegin = x xPointEnd = x + w @@ -180,24 +164,26 @@ class TKRubberPageControl : UIControl { } // 增加点击手势 - indexTap = UITapGestureRecognizer(target: self, action: #selector(TKRubberPageControl.tapValueChange(_: ))) - self.addGestureRecognizer(indexTap) + if indexTap == nil { + let tap = UITapGestureRecognizer(target: self, action: #selector(TKRubberPageControl.tapValueChange(_: ))) + addGestureRecognizer(tap) + indexTap = tap + } } // 重置控件 - func resetRubberIndicator(){ + public func resetRubberIndicator(){ changIndexToValue(0) smallBubbles.forEach {$0.removeFromSuperlayer()} smallBubbles.removeAll() - removeGestureRecognizer(indexTap) setUpView() } // 手势事件 - func tapValueChange(ges: UITapGestureRecognizer){ + @objc private func tapValueChange(ges: UITapGestureRecognizer){ let point = ges.locationInView(self) if point.y > yPointbegin && point.y < yPointEnd && point.x > xPointbegin && point.x < xPointEnd{ let index = Int(point.x - xPointbegin) / Int(styleConfig.smallBubbleMoveRadius) @@ -206,8 +192,8 @@ class TKRubberPageControl : UIControl { } // Index值变化 - func changIndexToValue(valueindex: Int){ - var index = valueindex + private func changIndexToValue(valueIndex: Int){ + var index = valueIndex if index >= numberOfpage{index = numberOfpage - 1} if index < 0{index = 0} if index == currentIndex {return} @@ -265,14 +251,12 @@ class TKRubberPageControl : UIControl { // MARK: - Small Bubble -class TKBubbleCell: CAShapeLayer { - - - var bubbleLayer = CAShapeLayer() - let bubbleScale : CGFloat = 0.5 - var lastDirection : TKMoveDirection! - var styleConfig : TKRubberPageControlConfig! +private class TKBubbleCell: CAShapeLayer { + private var bubbleLayer = CAShapeLayer() + private let bubbleScale : CGFloat = 0.5 + private var lastDirection : TKMoveDirection! + private var styleConfig : TKRubberPageControlConfig init(style: TKRubberPageControlConfig) { styleConfig = style @@ -280,11 +264,9 @@ class TKBubbleCell: CAShapeLayer { setupLayer() } - override init(layer: AnyObject) { - super.init(layer: layer) - } required init?(coder aDecoder: NSCoder) { + styleConfig = TKRubberPageControlConfig() super.init(coder: aDecoder) setupLayer() } @@ -356,7 +338,6 @@ class TKBubbleCell: CAShapeLayer { } - override func animationDidStop(anim: CAAnimation, finished flag: Bool) { if let animate = anim as? CAKeyframeAnimation{ if animate.keyPath == "position"{ diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 index 36c5ae5..0c280ca --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 ralph li +Copyright (c) 2015 TBXark Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 12f98b6..65e55ba --- a/README.md +++ b/README.md @@ -1,64 +1,54 @@ -# TKRubberIndicator +# TKRubberIndicator +> A rubber animation pagecontrol -在 dribbble 上面看到一个很不错的 page control,然后就上 github 上面搜索了一下,发现有 html 版的,和安卓版的(但是我看不懂 java 啊),虽然有个小伙伴建立了一个 Swift 项目但是里面并没有什么东西,然后我就决定自己仿一个. +[![Swift Version][swift-image]][swift-url] +[![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/TBXark/TKRubberIndicator/master/LICENSE) +[![CocoaPods](http://img.shields.io/cocoapods/v/TKRubberPageControl.svg?style=flat)](http://cocoapods.org/?q= TKRubberPageControl) +[![CocoaPods](http://img.shields.io/cocoapods/p/TKRubberPageControl.svg?style=flat)](http://cocoapods.org/?q= TKRubberPageControl) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Support](https://img.shields.io/badge/support-iOS%208%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/) -下面这个是 dribbble 的效果图 - +![](example.gif) -然后这个是实际效果图 +## Requirements - +- iOS 8.0+ +- Xcode 7.3 +## Installation +#### CocoaPods +You can use [CocoaPods](http://cocoapods.org/) to install `TKRubberPageControl` by adding it to your `Podfile`: -* Designed by [Valentyn Khenkin](https://dribbble.com/shots/2090803-Rubber-Indicator?list=searches&tag=indicator&offset=7) -* [Web 版](http://codepen.io/machycek/full/eNvyjb/) -* [安卓版](https://github.com/LyndonChin/AndroidRubberIndicator) - - -ps: 安卓版有超过1000个Star和300多分 fork, 我赵天日不服啊,iOS 的小伙伴们,让我看到你们的双手,给我一个星星吧 - - - -## 使用 - -还没有研究怎么搞 Cocoapod, 所以大家只能下载下来把文件 复制到工程里面使用,我把所有的类放到一个文件里面了 - - git clone https://github.com/TBXark/TKRubberIndicator.git - -## API - -#### 样式配置 - -|Key | Usage| -|---|---| -|smallBubbleSize|小球尺寸| -|mainBubbleSize|大球尺寸| -|bubbleXOffsetSpace|小球间距| -|bubbleYOffsetSpace|纵向间距| -|animationDuration|动画时长| -|backgroundColor|背景颜色| -|smallBubbleColor|小球颜色| -|mainBubbleColor|大球颜色| - -#### 初始化 - -**纯代码** - - init(frame: CGRect,count:Int,config:TKRubberIndicatorConfig = TKRubberIndicatorConfig()) +```ruby +platform :ios, '8.0' +use_frameworks! +pod 'TKRubberPageControl' +``` +To get the full benefits import `TKRubberPageControl` wherever you import UIKit -**XIB** +``` swift +import UIKit +import TKRubberPageControl +``` +#### Carthage +Create a `Cartfile` that lists the framework and run `carthage update`. Follow the [instructions](https://github.com/Carthage/Carthage#if-youre-building-for-ios) to add `$(SRCROOT)/Carthage/Build/iOS/TKRubberPageControl.framework` to an iOS project. - xib 的话,我平时很少用,使用 xib 只能用默认样式初始化,但是可以添加 runtime property 来改变 pageCount,如果想用 xib 又想自定义样式的话,要不就直接修改源代码,直接改变TKRubberIndicatorConfig的默认值 :) +``` +github "tbxark/TKRubberPageControl" +``` +#### Manually +1. Download and drop ```TKRubberPageControl.swift``` in your project. +2. Congratulations! +## Usage example -#### ValueChange事件 这里提供 闭包和 传统的 Target-Action 两种方式 +You can use closure or Target-Action to listen control event ``` - class ViewController: UIViewController { let page = TKRubberIndicator(frame: CGRectMake(100, 100, 200, 100), count: 6) @@ -66,7 +56,7 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - + self.view.backgroundColor = UIColor(red:0.553, green:0.376, blue:0.549, alpha:1) page.center = self.view.center page.valueChange = {(num) -> Void in @@ -78,7 +68,7 @@ class ViewController: UIViewController { // 可以变化 page 的个数 page.numberOfpage = 2 } - + @IBAction func pageCountChange(sender: UISegmentedControl) { page.numberOfpage = (sender.selectedSegmentIndex + 1) * 2 } @@ -93,20 +83,45 @@ class ViewController: UIViewController { ``` -## 注意 +### Base + +|Key | Usage| | +|---|---|---| +|smallBubbleSize|未选中小球尺寸|unselect small ball size| +|mainBubbleSize|选中大球尺寸|select big ball size| +|bubbleXOffsetSpace|小球间距|The distance between the ball| +|bubbleYOffsetSpace|纵向间距|bubble Y Offset Space| +|animationDuration|动画时长|animation duration| +|backgroundColor|背景颜色|control background color| +|smallBubbleColor|小球颜色|unselect small ball color| +|mainBubbleColor|大球颜色|select big ball color| + + +## Release History + +* 1.0.5 + Fix bug, add Cocoapod and Carthage support -当页数为 0 或者 1 的时候, (-,,-)PageControl 是没有意义的,所以我会报一个断言错误 +* 1.0.4 + Complete basic functions +## Contribute -## 关于我 +We would love for you to contribute to **TKRubberPageControl**, check the ``LICENSE`` file for more info. -* [weibo](http://weibo.com/tbxark) -* [blog](http://tbxark.github.io) +## Meta +TBXark – [@vfanx](https://twitter.com/vfanx) – tbxark@outlook.com -## 其他 -这里还有一个 动画的 Swift 做得 Switch 的集合大家也可以瞅瞅 [这里](https://github.com/TBXark/TKSwitcherCollection) +Distributed under the MIT license. See ``LICENSE`` for more information. -## 协议 +[https://github.com/TBXark](https://github.com/TBXark) - MIT +[swift-image]:https://img.shields.io/badge/swift-3.0-orange.svg +[swift-url]: https://swift.org/ +[license-image]: https://img.shields.io/badge/License-MIT-blue.svg +[license-url]: LICENSE +[travis-image]: https://img.shields.io/travis/dbader/node-datadog-metrics/master.svg?style=flat-square +[travis-url]: https://travis-ci.org/dbader/node-datadog-metrics +[codebeat-image]: https://codebeat.co/badges/c19b47ea-2f9d-45df-8458-b2d952fe9dad +[codebeat-url]: https://codebeat.co/projects/github-com-vsouza-awesomeios-com diff --git a/TKRubberIndicator.xcodeproj/project.pbxproj b/TKRubberIndicator.xcodeproj/project.pbxproj index 647fb11..edca8c2 100755 --- a/TKRubberIndicator.xcodeproj/project.pbxproj +++ b/TKRubberIndicator.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 6A555ECF1BDE277B00FC36E6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6A555ECD1BDE277B00FC36E6 /* Main.storyboard */; }; 6A555ED11BDE277B00FC36E6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A555ED01BDE277B00FC36E6 /* Assets.xcassets */; }; 6A555ED41BDE277B00FC36E6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6A555ED21BDE277B00FC36E6 /* LaunchScreen.storyboard */; }; + 6A9103F91D539B380009451B /* TKRubberPageControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A9103F81D539B380009451B /* TKRubberPageControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6A9103FE1D539B470009451B /* TKRubberPageControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A3D16F91BE9B0CA0018C0C4 /* TKRubberPageControl.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -26,6 +28,9 @@ 6A555ED01BDE277B00FC36E6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 6A555ED31BDE277B00FC36E6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 6A555ED51BDE277B00FC36E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6A9103F61D539B370009451B /* TKRubberPageControl.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TKRubberPageControl.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6A9103F81D539B380009451B /* TKRubberPageControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TKRubberPageControl.h; sourceTree = ""; }; + 6A9103FA1D539B380009451B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -36,6 +41,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6A9103F21D539B370009451B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -52,6 +64,7 @@ children = ( 6A3D16F81BE9B0CA0018C0C4 /* Classes */, 6A555EC81BDE277B00FC36E6 /* TKRubberIndicator */, + 6A9103F71D539B380009451B /* TKRubberPageControl */, 6A555EC71BDE277B00FC36E6 /* Products */, ); sourceTree = ""; @@ -60,6 +73,7 @@ isa = PBXGroup; children = ( 6A555EC61BDE277B00FC36E6 /* TKRubberIndicator.app */, + 6A9103F61D539B370009451B /* TKRubberPageControl.framework */, ); name = Products; sourceTree = ""; @@ -78,8 +92,28 @@ path = TKRubberIndicator; sourceTree = ""; }; + 6A9103F71D539B380009451B /* TKRubberPageControl */ = { + isa = PBXGroup; + children = ( + 6A9103F81D539B380009451B /* TKRubberPageControl.h */, + 6A9103FA1D539B380009451B /* Info.plist */, + ); + path = TKRubberPageControl; + sourceTree = ""; + }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 6A9103F31D539B370009451B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6A9103F91D539B380009451B /* TKRubberPageControl.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ 6A555EC51BDE277B00FC36E6 /* TKRubberIndicator */ = { isa = PBXNativeTarget; @@ -98,6 +132,24 @@ productReference = 6A555EC61BDE277B00FC36E6 /* TKRubberIndicator.app */; productType = "com.apple.product-type.application"; }; + 6A9103F51D539B370009451B /* TKRubberPageControl */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6A9103FD1D539B380009451B /* Build configuration list for PBXNativeTarget "TKRubberPageControl" */; + buildPhases = ( + 6A9103F11D539B370009451B /* Sources */, + 6A9103F21D539B370009451B /* Frameworks */, + 6A9103F31D539B370009451B /* Headers */, + 6A9103F41D539B370009451B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TKRubberPageControl; + productName = TKRubberPageControl; + productReference = 6A9103F61D539B370009451B /* TKRubberPageControl.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -112,6 +164,9 @@ 6A555EC51BDE277B00FC36E6 = { CreatedOnToolsVersion = 7.1; }; + 6A9103F51D539B370009451B = { + CreatedOnToolsVersion = 7.3.1; + }; }; }; buildConfigurationList = 6A555EC11BDE277B00FC36E6 /* Build configuration list for PBXProject "TKRubberIndicator" */; @@ -128,6 +183,7 @@ projectRoot = ""; targets = ( 6A555EC51BDE277B00FC36E6 /* TKRubberIndicator */, + 6A9103F51D539B370009451B /* TKRubberPageControl */, ); }; /* End PBXProject section */ @@ -144,6 +200,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6A9103F41D539B370009451B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -157,6 +220,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6A9103F11D539B370009451B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6A9103FE1D539B470009451B /* TKRubberPageControl.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ @@ -282,6 +353,50 @@ }; name = Release; }; + 6A9103FB1D539B380009451B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = TKRubberPageControl/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = TBXark.TKRubberPageControl; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 6A9103FC1D539B380009451B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = TKRubberPageControl/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = TBXark.TKRubberPageControl; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -303,6 +418,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6A9103FD1D539B380009451B /* Build configuration list for PBXNativeTarget "TKRubberPageControl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6A9103FB1D539B380009451B /* Debug */, + 6A9103FC1D539B380009451B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 6A555EBE1BDE277B00FC36E6 /* Project object */; diff --git a/TKRubberIndicator.xcodeproj/project.xcworkspace/xcuserdata/Tbxark.xcuserdatad/UserInterfaceState.xcuserstate b/TKRubberIndicator.xcodeproj/project.xcworkspace/xcuserdata/Tbxark.xcuserdatad/UserInterfaceState.xcuserstate index f0f447d..ef47333 100755 Binary files a/TKRubberIndicator.xcodeproj/project.xcworkspace/xcuserdata/Tbxark.xcuserdatad/UserInterfaceState.xcuserstate and b/TKRubberIndicator.xcodeproj/project.xcworkspace/xcuserdata/Tbxark.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TKRubberIndicator.xcodeproj/xcshareddata/xcschemes/TKRubberPageControl.xcscheme b/TKRubberIndicator.xcodeproj/xcshareddata/xcschemes/TKRubberPageControl.xcscheme new file mode 100755 index 0000000..c537b39 --- /dev/null +++ b/TKRubberIndicator.xcodeproj/xcshareddata/xcschemes/TKRubberPageControl.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TKRubberIndicator.xcodeproj/xcuserdata/Tbxark.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TKRubberIndicator.xcodeproj/xcuserdata/Tbxark.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index fe2b454..c3082ba 100755 --- a/TKRubberIndicator.xcodeproj/xcuserdata/Tbxark.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/TKRubberIndicator.xcodeproj/xcuserdata/Tbxark.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -2,4 +2,26 @@ + + + + + + + + + + + + + + diff --git a/TKRubberIndicator.xcodeproj/xcuserdata/Tbxark.xcuserdatad/xcschemes/xcschememanagement.plist b/TKRubberIndicator.xcodeproj/xcuserdata/Tbxark.xcuserdatad/xcschemes/xcschememanagement.plist index 7032888..6cc2b5b 100755 --- a/TKRubberIndicator.xcodeproj/xcuserdata/Tbxark.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/TKRubberIndicator.xcodeproj/xcuserdata/Tbxark.xcuserdatad/xcschemes/xcschememanagement.plist @@ -9,6 +9,11 @@ orderHint 0 + TKRubberPageControl.xcscheme_^#shared#^_ + + orderHint + 1 + SuppressBuildableAutocreation @@ -17,6 +22,11 @@ primary + 6A9103F51D539B370009451B + + primary + + diff --git a/TKRubberIndicator/Base.lproj/LaunchScreen.storyboard b/TKRubberIndicator/Base.lproj/LaunchScreen.storyboard index 1447b3c..3153078 100755 --- a/TKRubberIndicator/Base.lproj/LaunchScreen.storyboard +++ b/TKRubberIndicator/Base.lproj/LaunchScreen.storyboard @@ -1,8 +1,8 @@ - + - + @@ -16,8 +16,23 @@ - + + + + + + + diff --git a/TKRubberIndicator/Base.lproj/Main.storyboard b/TKRubberIndicator/Base.lproj/Main.storyboard index 591d0c9..ee3eb3b 100755 --- a/TKRubberIndicator/Base.lproj/Main.storyboard +++ b/TKRubberIndicator/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -20,13 +20,12 @@ - - + + - - + @@ -34,7 +33,6 @@ - diff --git a/TKRubberIndicator/Classes/TKRubberIndicator.swift b/TKRubberIndicator/Classes/TKRubberIndicator.swift deleted file mode 100755 index 311320a..0000000 --- a/TKRubberIndicator/Classes/TKRubberIndicator.swift +++ /dev/null @@ -1,373 +0,0 @@ -// -// TKRubberIndicator.swift -// TKRubberIndicator -// -// Created by Tbxark on 15/10/26. -// Copyright © 2015年 TBXark. All rights reserved. -// - -import UIKit - - -// ValueChange -typealias UIControlValueChangeClosure = (Any) -> Void - -// 快速获得 W 和 H -extension UIView{ - var w : CGFloat{ - return self.bounds.width - } - var h : CGFloat{ - return self.bounds.height - } -} - - -// MARK: - MoveDorection -// 运动方向 - -enum TKMoveDirection{ - case left - case right - func toBool() -> Bool{ - switch self{ - case .left: - return true - case .right: - return false - } - } -} - -// MARK: - TKRubberIndicatorConfig -// 样式配置 (含默认配置) - -struct TKRubberIndicatorConfig { - // 小球尺寸 - var smallBubbleSize :CGFloat = 16 - // 大球尺寸 - var mainBubbleSize :CGFloat = 40 - // 小球间距 - var bubbleXOffsetSpace :CGFloat = 12 - // 纵向间距 - var bubbleYOffsetSpace :CGFloat = 8 - // 动画时长 - var animationDuration :CFTimeInterval = 0.2 - // 小球运动半径 - var smallBubbleMoveRadius : CGFloat {return smallBubbleSize + bubbleXOffsetSpace} - - // 横条背景颜色 - var backgroundColor : UIColor = UIColor(red:0.357, green:0.196, blue:0.337, alpha:1) - // 小球颜色 - var smallBubbleColor : UIColor = UIColor(red:0.961, green:0.561, blue:0.518, alpha:1) - // 大球颜色 - var bigBubbleColor : UIColor = UIColor(red:0.788, green:0.216, blue:0.337, alpha:1) -} - - - - -// MARK: PageControl -class TKRubberIndicator : UIControl { - - // 页数 - var numberOfpage : Int = 5{ - didSet{ - if oldValue != numberOfpage{ - resetRubberIndicator() - } - } - } - - // 当前 Index - var currentIndex = 0 - - // 事件闭包 - var valueChange : UIControlValueChangeClosure? - // 样式配置 - var styleConfig : TKRubberIndicatorConfig! - - //手势 - var indexTap : UITapGestureRecognizer! - - - // 所有图层 - var smallBubbles = Array() - var backgroundLayer = CAShapeLayer() - var mainBubble = CAShapeLayer() - var backLineLayer = CAShapeLayer() - - // 大球缩放比例 - let bubbleScale :CGFloat = 1/3.0 - - // 存储计算用的 - var xPointbegin : CGFloat = 0 - var xPointEnd : CGFloat = 0 - var yPointbegin : CGFloat = 0 - var yPointEnd : CGFloat = 0 - - - init(frame: CGRect,count:Int,config:TKRubberIndicatorConfig = TKRubberIndicatorConfig()) { - numberOfpage = count - styleConfig = config - super.init(frame:frame) - self.setUpView() - assert(numberOfpage > 1, "Page count should larger than 1") - } - - required init?(coder aDecoder: NSCoder) { - styleConfig = TKRubberIndicatorConfig() - super.init(coder: aDecoder) - self.setUpView() - assert(numberOfpage > 1, "Page count should larger than 1") - } - - private func setUpView(){ - - // 一些奇怪的位置计算 - - let y = (self.h - (styleConfig.smallBubbleSize + 2 * styleConfig.bubbleYOffsetSpace))/2 - let w = CGFloat(numberOfpage - 2) * styleConfig.smallBubbleSize + styleConfig.mainBubbleSize + CGFloat(numberOfpage) * styleConfig.bubbleXOffsetSpace - let h = styleConfig.smallBubbleSize + styleConfig.bubbleYOffsetSpace * 2 - let x = (self.w - w)/2 - - xPointbegin = x - xPointEnd = x + w - yPointbegin = y - yPointEnd = y + h - - let lineFrame = CGRectMake(x,y,w,h) - let frame = CGRectMake(x, y - (styleConfig.mainBubbleSize - h)/2, styleConfig.mainBubbleSize, styleConfig.mainBubbleSize) - var layerFrame = CGRectInset(frame, styleConfig.bubbleYOffsetSpace , styleConfig.bubbleYOffsetSpace) - - - // 背景的横线 - backLineLayer.path = UIBezierPath(roundedRect:lineFrame, cornerRadius:h/2).CGPath - backLineLayer.fillColor = styleConfig.backgroundColor.CGColor - self.layer.addSublayer(backLineLayer) - - - // 大球背景的圈 - backgroundLayer.path = UIBezierPath(ovalInRect: frame).CGPath - backgroundLayer.fillColor = styleConfig.backgroundColor.CGColor - backgroundLayer.zPosition = -1 - self.layer.addSublayer(backgroundLayer) - - - - // 大球 - let origin = layerFrame.origin - layerFrame.origin = CGPointZero - mainBubble.path = UIBezierPath(ovalInRect: layerFrame).CGPath - mainBubble.fillColor = styleConfig.bigBubbleColor.CGColor - layerFrame.origin = origin - mainBubble.frame = layerFrame - mainBubble.zPosition = 100 - self.layer.addSublayer(mainBubble) - - - // 生成小球 - let bubbleOffset = styleConfig.smallBubbleSize + styleConfig.bubbleXOffsetSpace - var bubbleFrame = CGRectMake(x + styleConfig.bubbleXOffsetSpace + bubbleOffset , y + styleConfig.bubbleYOffsetSpace, styleConfig.smallBubbleSize, styleConfig.smallBubbleSize) - for _ in 0..<(numberOfpage-1){ - let smallBubble = TKBubbleCell(style: styleConfig) - smallBubble.frame = bubbleFrame - self.layer.addSublayer(smallBubble) - smallBubbles.append(smallBubble) - bubbleFrame.origin.x += bubbleOffset - smallBubble.zPosition = 1 - } - - // 增加点击手势 - indexTap = UITapGestureRecognizer(target: self, action: "tapValueChange:") - self.addGestureRecognizer(indexTap) - } - - - // 重置控件 - func resetRubberIndicator(){ - changIndexToValue(0) - smallBubbles.forEach {$0.removeFromSuperlayer()} - smallBubbles.removeAll() - removeGestureRecognizer(indexTap) - setUpView() - } - - - - // 手势事件 - func tapValueChange(ges:UITapGestureRecognizer){ - let point = ges.locationInView(self) - if point.y > yPointbegin && point.y < yPointEnd && point.x > xPointbegin && point.x < xPointEnd{ - let index = Int(point.x - xPointbegin) / Int(styleConfig.smallBubbleMoveRadius) - changIndexToValue(index) - } - } - - // Index值变化 - func changIndexToValue(var index:Int){ - - if index >= numberOfpage{index = numberOfpage - 1} - if index < 0{index = 0} - if index == currentIndex {return} - - let direction = (currentIndex > index) ? TKMoveDirection.right : TKMoveDirection.left - let point = CGPointMake(xPointbegin + styleConfig.smallBubbleMoveRadius * CGFloat(index) + styleConfig.mainBubbleSize/2, yPointbegin - styleConfig.bubbleYOffsetSpace/2) - let range = (currentIndex < index) ? (currentIndex+1)...index : index...(currentIndex-1) - - for index in range{ - let smallBubbleIndex = (direction.toBool()) ? (index - 1) : (index) - let smallBubble = smallBubbles[smallBubbleIndex] - smallBubble.positionChange(direction, radius: styleConfig.smallBubbleMoveRadius / 2, duration:styleConfig.animationDuration,beginTime:CACurrentMediaTime()) - } - currentIndex = index - mainBubblePositionChange(direction, point: point, duration: styleConfig.animationDuration) - - // 可以使用 Target-Action 监听事件 - sendActionsForControlEvents(UIControlEvents.ValueChanged) - // 也可以使用 闭包 监听事件 - valueChange?(currentIndex) - - } - - // 大球动画 - private func mainBubblePositionChange(direction:TKMoveDirection,var point:CGPoint,duration:Double){ - - // 大球缩放动画 - let bubbleTransformAnim = CAKeyframeAnimation(keyPath:"transform") - bubbleTransformAnim.values = [NSValue(CATransform3D: CATransform3DIdentity), - NSValue(CATransform3D: CATransform3DMakeScale(bubbleScale, bubbleScale, 1)), - NSValue(CATransform3D: CATransform3DIdentity)] - bubbleTransformAnim.keyTimes = [0, 0.5, 1] - bubbleTransformAnim.duration = duration - - - // 大球移动动画,用隐式动画大球的位置会真正的改变 - CATransaction.begin() - CATransaction.setAnimationDuration(duration) - point.y += styleConfig.mainBubbleSize/2 - mainBubble.position = point - - point.y = 0 - point.x = (xPointEnd - xPointbegin - styleConfig.bubbleXOffsetSpace/2) / CGFloat(numberOfpage) * CGFloat(currentIndex) - (styleConfig.bubbleYOffsetSpace / 4) - backgroundLayer.position = point - CATransaction.commit() - - mainBubble.addAnimation(bubbleTransformAnim, forKey: "Scale") - } -} - - - - - - - -// MARK: - Small Bubble -class TKBubbleCell: CAShapeLayer { - - - var bubbleLayer = CAShapeLayer() - let bubbleScale :CGFloat = 0.5 - var lastDirection : TKMoveDirection! - var styleConfig : TKRubberIndicatorConfig! - - - init(style:TKRubberIndicatorConfig) { - styleConfig = style - super.init() - setupLayer() - } - - override init(layer: AnyObject) { - super.init(layer: layer) - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - setupLayer() - } - - private func setupLayer(){ - self.frame = CGRectMake(0, 0, styleConfig.smallBubbleSize, styleConfig.smallBubbleSize) - - bubbleLayer.path = UIBezierPath(ovalInRect: self.bounds).CGPath - bubbleLayer.fillColor = styleConfig.smallBubbleColor.CGColor - bubbleLayer.strokeColor = styleConfig.backgroundColor.CGColor - bubbleLayer.lineWidth = styleConfig.bubbleXOffsetSpace / 8 - - self.addSublayer(bubbleLayer) - } - - // beginTime 本来是留给小球轮播用的,但是效果不好就没用了 - func positionChange(direction:TKMoveDirection,radius:CGFloat,duration:CFTimeInterval,beginTime:CFTimeInterval){ - - let toLeft = direction.toBool() - let movePath = UIBezierPath() - var center = CGPointZero - let startAngle = toLeft ? 0 : CGFloat(M_PI) - let endAngle = toLeft ? CGFloat(M_PI) : 0 - center.x += radius * (toLeft ? -1 : 1) - lastDirection = direction - - movePath.addArcWithCenter(center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: toLeft) - - // 小球整体沿着圆弧运动,但是当圆弧运动动画合形变动画叠加在一起的时候,就没有了向心作用,所以就把形变动画放在子 Layer 里面 - let positionAnimation = CAKeyframeAnimation(keyPath: "position") - positionAnimation.duration = duration - positionAnimation.beginTime = beginTime - positionAnimation.additive = true; - positionAnimation.calculationMode = kCAAnimationPaced; - positionAnimation.rotationMode = kCAAnimationRotateAuto; - positionAnimation.path = movePath.CGPath - positionAnimation.fillMode = kCAFillModeBoth - positionAnimation.delegate = self - - - // 小球变形动画,小球变形实际上只是 Y 轴上的 Scale - let bubbleTransformAnim = CAKeyframeAnimation(keyPath:"transform") - bubbleTransformAnim.values = [NSValue(CATransform3D: CATransform3DIdentity), - NSValue(CATransform3D: CATransform3DMakeScale(1,bubbleScale, 1)), - NSValue(CATransform3D: CATransform3DIdentity)] - bubbleTransformAnim.keyTimes = [0, 0.5, 1] - bubbleTransformAnim.duration = duration - - bubbleTransformAnim.beginTime = beginTime - - bubbleLayer.addAnimation(bubbleTransformAnim, forKey: "Scale") - self.addAnimation(positionAnimation, forKey: "Position") - } - - private func shakeAnimate(){ - // TODO: 未解决,闪烁问题 - // 没找到办法在球做完圆弧运动后保持状态的办法,这里只能关掉隐式动画强行改变位置 - CATransaction.begin() - CATransaction.setDisableActions(true) - self.opacity = 0 - var point = self.position - point.x += (styleConfig.smallBubbleSize + styleConfig.bubbleXOffsetSpace) * CGFloat(lastDirection.toBool() ? -1 : 1) - self.position = point - self.opacity = 1 - CATransaction.commit() - - // 最后让小球鬼畜的抖动一下 - let bubbleShakeAnim = CAKeyframeAnimation(keyPath: "position") - bubbleShakeAnim.duration = 0.01 - bubbleShakeAnim.values = [NSValue(CGPoint: CGPointMake(0, 0)), - NSValue(CGPoint: CGPointMake(0, 4)), - NSValue(CGPoint: CGPointMake(0, 0)), - NSValue(CGPoint: CGPointMake(0,-4)), - NSValue(CGPoint: CGPointMake(0, 0)),] - bubbleShakeAnim.repeatCount = 10 - self.bubbleLayer.addAnimation(bubbleShakeAnim, forKey: "Shake") - } - - override func animationDidStop(anim: CAAnimation, finished flag: Bool) { - if let animate = anim as? CAKeyframeAnimation{ - if animate.keyPath == "position"{ - shakeAnimate() - } - } - } - -} \ No newline at end of file diff --git a/TKRubberIndicator/Info.plist b/TKRubberIndicator/Info.plist index 6905cc6..3c4928f 100755 --- a/TKRubberIndicator/Info.plist +++ b/TKRubberIndicator/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + 1.0.6 CFBundleSignature ???? CFBundleVersion diff --git a/TKRubberIndicator/ViewController.swift b/TKRubberIndicator/ViewController.swift index 841a6ca..9646c86 100755 --- a/TKRubberIndicator/ViewController.swift +++ b/TKRubberIndicator/ViewController.swift @@ -10,7 +10,7 @@ import UIKit class ViewController: UIViewController { - let page = TKRubberPageControl(frame: CGRectMake(100, 100, 200, 100), count: 6) + let page = TKRubberPageControl(frame: CGRectMake(100, 100, 200, 100), count: 3) override func viewDidLoad() { super.viewDidLoad() @@ -25,11 +25,11 @@ class ViewController: UIViewController { self.view.addSubview(page) - page.numberOfpage = 2 + page.numberOfpage = 3 } @IBAction func pageCountChange(sender: UISegmentedControl) { - page.numberOfpage = (sender.selectedSegmentIndex + 1) * 2 + page.numberOfpage = sender.selectedSegmentIndex + 3 } func targetActionValueChange(page:TKRubberPageControl){ print("Target-Action : Page is \(page.currentIndex)") diff --git a/TKRubberIndicator/rubberindicator.gif b/TKRubberIndicator/rubberindicator.gif old mode 100644 new mode 100755 diff --git a/TKRubberPageControl.podspec b/TKRubberPageControl.podspec old mode 100644 new mode 100755 index 55520b1..b0f153d --- a/TKRubberPageControl.podspec +++ b/TKRubberPageControl.podspec @@ -1,10 +1,10 @@ Pod::Spec.new do |s| s.name = "TKRubberPageControl" - s.version = "1.0.5" + s.version = "1.0.6" s.summary = "A rubber pagec ontrol in Swift." + s.license = { :type => 'MIT License', :file => 'LICENSE' } # 协议 s.homepage = "https://github.com/TBXark/TKRubberIndicator" - s.license = { :type => 'MIT License', :file => 'LICENSE' } - s.author = { "vfanx" => "tbxark@outlook.com" } + s.author = { "TBXark" => "tbxark@outlook.com" } s.source = { :git => "https://github.com/TBXark/TKRubberIndicator.git", :tag => s.version } s.platform = :ios, '8.0' s.source_files = 'Classes/TKRubberPageControl.swift' diff --git a/TKRubberPageControl/Info.plist b/TKRubberPageControl/Info.plist new file mode 100755 index 0000000..61718cd --- /dev/null +++ b/TKRubberPageControl/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.6 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/TKRubberPageControl/TKRubberPageControl.h b/TKRubberPageControl/TKRubberPageControl.h new file mode 100755 index 0000000..7b88678 --- /dev/null +++ b/TKRubberPageControl/TKRubberPageControl.h @@ -0,0 +1,19 @@ +// +// TKRubberPageControl.h +// TKRubberPageControl +// +// Created by Tbxark on 8/4/16. +// Copyright © 2016 TBXark. All rights reserved. +// + +#import + +//! Project version number for TKRubberPageControl. +FOUNDATION_EXPORT double TKRubberPageControlVersionNumber; + +//! Project version string for TKRubberPageControl. +FOUNDATION_EXPORT const unsigned char TKRubberPageControlVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/example.gif b/example.gif new file mode 100755 index 0000000..c3c3a31 Binary files /dev/null and b/example.gif differ