├── .DS_Store ├── .gitignore ├── Licence.txt ├── Package.swift ├── README.md ├── Sources └── SwiftyOverlay │ ├── GDOverlay.swift │ └── Utilities.swift ├── SwiftyOverlay.podspec ├── SwiftyOverlay.podspec~ ├── SwiftyOverlay.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── saeid.basirnia.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── saeid.basirnia.xcuserdatad │ └── xcschemes │ │ └── xcschememanagement.plist │ └── saeid.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist └── Tests └── SwiftyOverlayTests └── SwiftyOverlayTests.swift /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeid/SwiftyOverlay/e38874af1ecd58e498736e2bebfc7aebaf8ecf68/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | -------------------------------------------------------------------------------- /Licence.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Saeid Basirnia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "SwiftyOverlay", 8 | dependencies: [], 9 | targets: [ 10 | .target( 11 | name: "SwiftyOverlay", 12 | dependencies: []), 13 | .testTarget( 14 | name: "SwiftyOverlayTests", 15 | dependencies: ["SwiftyOverlay"]), 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftyOverlay 2 | A component to show instructions and info on UI at run time with easy to setup and customizable API. 3 | 4 | Supported Components are : `UITabbarItem`, `TableView`, `TabbarView`, all `UIView` controls and components! 5 | 6 | For demo project check [this repo](https://github.com/saeid/SwiftyGuideOverlay) 7 | 8 | ![2](https://cloud.githubusercontent.com/assets/9967486/21859393/a6fbe282-d841-11e6-9271-e0e9e9c6bb6c.gif) 9 | ![1](https://cloud.githubusercontent.com/assets/9967486/21859399/ac3822a6-d841-11e6-9272-64c553630e1c.gif) 10 | 11 | 12 | ## Requirements 13 | - Xcode 9+ 14 | - Swift 4+ 15 | - iOS 9+ 16 | 17 | 18 | ## Installation 19 | 20 | ## Swift Package Manager 21 | 22 | ```swift 23 | .package(url: "https://github.com/saeid/SwiftyOverlay.git", from: "1.1.14") 24 | ``` 25 | 26 | ## Cocoapods 27 | 28 | ``` 29 | source 'https://github.com/CocoaPods/Specs.git' 30 | platform :ios, '9.0' 31 | use_frameworks! 32 | 33 | target '' do 34 | pod 'SwiftyOverlay' 35 | end 36 | ``` 37 | pod update 38 | pod install 39 | 40 | ## Usage 41 | 42 | Inherit `SkipOverlayDelegate` 43 | ```swift 44 | class ViewController: UIViewController, SkipOverlayDelegate 45 | ``` 46 | 47 | Create an instance of GDOverlay 48 | ```swift 49 | var overlay: GDOverlay = GDOverlay() 50 | ``` 51 | 52 | Set delegate 53 | ```swift 54 | overlay.delegate = self 55 | ``` 56 | 57 | Set properties 58 | ```swift 59 | overlay.arrowColor = UIColor.red 60 | overlay.arrowWidth = 2.0 61 | overlay.lineType = LineType.line_bubble 62 | 63 | ... 64 | 65 | // Full properties list can be found on sample project 66 | 67 | ``` 68 | 69 | Now call Overlay View Skip function to show! 70 | ```swift 71 | onSkipSignal() 72 | ``` 73 | 74 | Override `onSkipSignal` function 75 | ```swift 76 | func onSkipSignal(){ 77 | /// Add an attributed string over the screen 78 | overlay.drawOverlay(desc: NSMutableAttributedString) 79 | 80 | /// TableView 81 | overlay.drawOverlay(to: self.tableView, section: 0, row: 0, desc: "Description ...") 82 | 83 | /// UIBarButtonItem 84 | overlay.drawOverlay(to: barButtonItem, desc: "Description ...") 85 | 86 | /// Any other views 87 | overlay.drawOverlay(to: self.someView, desc: "Description ...", isCircle: true) 88 | 89 | /// TabBar Items 90 | overlay.drawOverlay(to: self.tabbarView, item: 0, desc: "Description ...") 91 | 92 | /// For StackViews, Eg. first view of stackview 93 | let targetView = stackView.arrangedSubviews[0] 94 | o.drawOverlay(to: targetView, desc: "Description ...", isCircle: true) 95 | } 96 | ``` 97 | 98 | ## Licence 99 | SwiftyHelpOverlay is available under the MIT license. See the [LICENSE.txt](https://github.com/SaeidBsn/SwiftyGuideOverlay/blob/master/Licence.txt) file for more info. 100 | 101 | -------------------------------------------------------------------------------- /Sources/SwiftyOverlay/GDOverlay.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GDOverlay.swift 3 | // SwiftyGuideOverlay 4 | // 5 | // Created by Saeid Basirnia on 8/16/16. 6 | // Copyright © 2016 Saeidbsn. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol SkipOverlayDelegate: AnyObject { 12 | func onSkipSignal() 13 | } 14 | 15 | public final class GDOverlay: UIView { 16 | //MARK: - Attributes 17 | fileprivate var _backColor: UIColor = UIColor.black.withAlphaComponent(0.8) 18 | 19 | public var backColor: UIColor { 20 | get { 21 | return _backColor 22 | } 23 | set { 24 | _backColor = newValue 25 | } 26 | } 27 | 28 | fileprivate var _boxBackColor: UIColor = UIColor.white.withAlphaComponent(0.05) 29 | public var boxBackColor: UIColor { 30 | get { 31 | return _boxBackColor 32 | } 33 | set { 34 | _boxBackColor = newValue 35 | } 36 | } 37 | 38 | fileprivate var _boxBorderColor: UIColor = UIColor.white 39 | public var boxBorderColor: UIColor { 40 | get { 41 | return _boxBorderColor 42 | } 43 | set { 44 | _boxBorderColor = newValue 45 | } 46 | } 47 | 48 | fileprivate var _showBorder: Bool = true 49 | public var showBorder: Bool { 50 | get { 51 | return _showBorder 52 | } 53 | set { 54 | _showBorder = newValue 55 | } 56 | } 57 | 58 | fileprivate var _lineType: LineType = .dash_bubble 59 | public var lineType: LineType { 60 | get { 61 | return _lineType 62 | } 63 | set { 64 | _lineType = newValue 65 | } 66 | } 67 | 68 | fileprivate var _arrowColor: UIColor = UIColor.white 69 | public var arrowColor: UIColor { 70 | get { 71 | return _arrowColor 72 | } 73 | set { 74 | _arrowColor = newValue 75 | } 76 | } 77 | 78 | fileprivate var _headColor: UIColor = UIColor.white 79 | public var headColor: UIColor { 80 | get { 81 | return _headColor 82 | } 83 | set { 84 | _headColor = newValue 85 | } 86 | } 87 | 88 | fileprivate var _arrowWidth: CGFloat = 2.0 89 | public var arrowWidth: CGFloat { 90 | get { 91 | return _arrowWidth 92 | } 93 | set { 94 | _arrowWidth = newValue 95 | } 96 | } 97 | 98 | fileprivate var _headRadius: CGFloat = 4.0 99 | public var headRadius: CGFloat { 100 | get { 101 | return _headRadius 102 | } 103 | set { 104 | _headRadius = newValue 105 | } 106 | } 107 | 108 | fileprivate var _highlightView: Bool = false 109 | public var highlightView: Bool { 110 | get { 111 | return _highlightView 112 | } 113 | set { 114 | _highlightView = newValue 115 | } 116 | } 117 | 118 | //MARK: - Self Init 119 | public weak var delegate: SkipOverlayDelegate? = nil 120 | fileprivate var helpView: UIView! 121 | 122 | public init(){ 123 | super.init(frame: CGRect.zero) 124 | 125 | self.frame = self.topView?.frame ?? CGRect.zero 126 | self.backgroundColor = UIColor.clear 127 | } 128 | 129 | required public init?(coder aDecoder: NSCoder) { 130 | super.init(coder: aDecoder) 131 | } 132 | 133 | private func calculateCenter() -> CGPoint { 134 | let targetRect = helpView.convert(helpView.bounds , to: topView) 135 | return targetRect.center 136 | } 137 | 138 | private func initViews(_ circle: Bool, textOnly: Bool = false) { 139 | if !textOnly { 140 | let targetCenter: CGPoint = calculateCenter() 141 | self.createBackgroundView() 142 | self.createContainerView() 143 | 144 | self.topView?.addSubview(self) 145 | setupContainerViewConstraints(to: targetCenter) 146 | 147 | layoutIfNeeded() 148 | if _highlightView { 149 | self.unmaskView(targetCenter, isCircle: circle) 150 | } 151 | 152 | self.createTargetView(center: targetCenter) 153 | } else { 154 | self.createBackgroundView() 155 | self.createContainerView() 156 | 157 | self.topView?.addSubview(self) 158 | setupContainerViewConstraints() 159 | 160 | layoutIfNeeded() 161 | } 162 | } 163 | 164 | public func drawOverlay(to barButtonItem: UIBarButtonItem, desc: NSAttributedString){ 165 | if let barView = barButtonItem.value(forKey: "view") as? UIView { 166 | let barFrame = barView.frame 167 | let windowRect = barView.convert(barFrame, to: topView) 168 | let v = UIView() 169 | v.frame = windowRect 170 | self.addSubview(v) 171 | helpView = v 172 | } 173 | 174 | descLabel.attributedText = desc 175 | initViews(true) 176 | } 177 | 178 | public func drawOverlay(to tabbarView: UITabBar, item: Int, desc: NSAttributedString){ 179 | var vs = tabbarView.subviews.filter { $0.isUserInteractionEnabled } 180 | vs = vs.sorted(by: { $0.frame.minX < $1.frame.minX }) 181 | 182 | var windowRect = vs[item].convert(vs[0].frame, to: topView) 183 | 184 | if UIDevice.current.userInterfaceIdiom == .pad { 185 | windowRect.origin.x = vs[item].frame.minX 186 | windowRect.size.width = vs[item].frame.width 187 | } 188 | 189 | let v = UIView() 190 | v.frame = windowRect 191 | self.addSubview(v) 192 | 193 | helpView = v 194 | 195 | descLabel.attributedText = desc 196 | initViews(false) 197 | } 198 | 199 | public func drawOverlay(to tableView: UITableView, section: Int, row: Int, desc: NSAttributedString) { 200 | let indexPath: IndexPath = IndexPath(row: row, section: section) 201 | let tableRect = tableView.rectForRow(at: indexPath) 202 | let windowRect = tableView.convert(tableRect, to: topView) 203 | 204 | let v = UIView() 205 | v.frame = windowRect 206 | self.addSubview(v) 207 | 208 | helpView = v 209 | 210 | descLabel.attributedText = desc 211 | initViews(false) 212 | } 213 | 214 | 215 | public func drawOverlay(desc: NSAttributedString) { 216 | initViews(false, textOnly: true) 217 | descLabel.attributedText = desc 218 | } 219 | 220 | public func drawOverlay(to view: UIView, desc: NSAttributedString, isCircle: Bool = true) { 221 | let windowRect = view.convert(view.bounds , to: topView) 222 | let v = UIView() 223 | v.frame = windowRect 224 | self.addSubview(v) 225 | 226 | helpView = v 227 | 228 | initViews(isCircle) 229 | descLabel.attributedText = desc 230 | } 231 | 232 | //MARK: - Background View 233 | fileprivate var backgroundView: UIView! 234 | private func createBackgroundView(){ 235 | backgroundView = UIView() 236 | backgroundView.frame = self.frame 237 | backgroundView.isUserInteractionEnabled = true 238 | backgroundView.backgroundColor = UIColor.clear 239 | backgroundView.backgroundColor = _backColor 240 | 241 | self.addSubview(backgroundView) 242 | self.setupGestures() 243 | } 244 | 245 | private func setupGestures() { 246 | let tapGest = UITapGestureRecognizer(target: self, action: #selector(gotoNext(_:))) 247 | tapGest.numberOfTapsRequired = 1 248 | tapGest.numberOfTouchesRequired = 1 249 | 250 | self.backgroundView.addGestureRecognizer(tapGest) 251 | } 252 | 253 | @objc private func gotoNext(_ sender: UIGestureRecognizer) { 254 | self.removeFromSuperview() 255 | self.backgroundView.removeFromSuperview() 256 | self.delegate?.onSkipSignal() 257 | } 258 | 259 | //MARK: - Description Label 260 | fileprivate var descLabel: UILabel = { 261 | let lbl = UILabel() 262 | lbl.numberOfLines = 0 263 | lbl.lineBreakMode = .byWordWrapping 264 | lbl.sizeToFit() 265 | lbl.translatesAutoresizingMaskIntoConstraints = false 266 | lbl.textAlignment = .center 267 | return lbl 268 | }() 269 | 270 | private lazy var getLabelHeight: CGFloat = { 271 | let lblHeight = self.descLabel.frame.height 272 | return lblHeight 273 | }() 274 | 275 | //MARK: - Container View 276 | fileprivate var contView: UIView! 277 | private func createContainerView() { 278 | guard let topView = topView else { return } 279 | 280 | contView = UIView() 281 | contView.frame = CGRect(x: 0, y: 0, width: topView.frame.width - 60, height: 50) 282 | contView.backgroundColor = _boxBackColor 283 | if _showBorder { 284 | contView.layer.borderColor = _boxBorderColor.cgColor 285 | contView.layer.borderWidth = 2 286 | contView.layer.cornerRadius = 5 287 | } 288 | contView.translatesAutoresizingMaskIntoConstraints = false 289 | contView.addSubview(descLabel) 290 | backgroundView.addSubview(contView) 291 | setupLabelConstraints() 292 | } 293 | 294 | //MARK: - Tools 295 | private func unmaskView(_ targetPoint: CGPoint, isCircle: Bool) { 296 | let maskLayer = CAShapeLayer() 297 | let path = CGMutablePath() 298 | 299 | let radius: CGFloat = isCircle ? (max(helpView.frame.width + 20, helpView.frame.height + 10)) / 2 : 0 300 | let clipPath: CGPath = UIBezierPath(roundedRect: CGRect(x: helpView.frame.origin.x - 20, y: helpView.frame.origin.y - 10, width: helpView.frame.width + 40, height: helpView.frame.height + 20), cornerRadius: radius).cgPath 301 | 302 | path.addPath(clipPath) 303 | path.addRect(CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)) 304 | 305 | maskLayer.backgroundColor = UIColor.black.cgColor 306 | maskLayer.path = path 307 | maskLayer.fillRule = CAShapeLayerFillRule.evenOdd 308 | 309 | backgroundView.layer.mask = maskLayer 310 | backgroundView.clipsToBounds = false 311 | } 312 | } 313 | 314 | // MARK: - setup constraints 315 | extension GDOverlay { 316 | fileprivate func setupLabelConstraints(){ 317 | descLabel.leftAnchor.constraint(equalTo: contView.leftAnchor, constant: 10.0).isActive = true 318 | descLabel.rightAnchor.constraint(equalTo: contView.rightAnchor, constant: -10.0).isActive = true 319 | descLabel.topAnchor.constraint(equalTo: contView.topAnchor, constant: 10.0).isActive = true 320 | descLabel.bottomAnchor.constraint(equalTo: contView.bottomAnchor, constant: -10.0).isActive = true 321 | } 322 | 323 | fileprivate func setupContainerViewConstraints() { 324 | let centerX = contView.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0) 325 | centerX.isActive = true 326 | let centerY = contView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0) 327 | centerY.isActive = true 328 | 329 | let right = contView.rightAnchor.constraint(equalTo: rightAnchor, constant: -16) 330 | right.isActive = true 331 | let left = contView.leftAnchor.constraint(equalTo: leftAnchor, constant: 16) 332 | left.isActive = true 333 | 334 | topView?.addConstraints([centerY, centerX, right, left]) 335 | } 336 | 337 | fileprivate func setupContainerViewConstraints(to point: CGPoint) { 338 | let section = setSection(point) 339 | let consts = setSectionPoint(section) 340 | 341 | topView?.addConstraints(consts) 342 | } 343 | } 344 | 345 | //MARK: - Create and calculate points 346 | extension GDOverlay{ 347 | private func calcCenterPoint(_ start: CGPoint, end: CGPoint) -> CGPoint { 348 | let x = (start.x + end.x) / 2 349 | let y = (start.y + end.y) / 2 350 | 351 | return CGPoint(x: x, y: y) 352 | } 353 | 354 | fileprivate func createTargetView(center: CGPoint) { 355 | let section = setSection(center) 356 | var startPoint: CGPoint! 357 | var endPoint: CGPoint! 358 | var controlPoint: CGPoint! 359 | 360 | let dir = LineDirection.randomDir 361 | let offsetTop: CGFloat = highlightView ? 20.0 : 0.0 362 | let offsetBottom: CGFloat = highlightView ? -20.0 : 0.0 363 | 364 | switch section { 365 | case 0, 1: 366 | if dir == .left { 367 | startPoint = CGPoint(x: contView.frame.midX - 50, y: contView.frame.minY - 10) 368 | endPoint = CGPoint(x: helpView.frame.midX, y: helpView.frame.maxY + offsetTop) 369 | 370 | let cp = calcCenterPoint(startPoint, end: endPoint) 371 | controlPoint = CGPoint(x: cp.x - 50, y: cp.y) 372 | } else { 373 | startPoint = CGPoint(x: contView.frame.midX, y: contView.frame.minY - 20) 374 | endPoint = CGPoint(x: helpView.frame.midX + 25, y: helpView.frame.maxY + offsetTop) 375 | 376 | let cp = calcCenterPoint(startPoint, end: endPoint) 377 | controlPoint = CGPoint(x: cp.x + 50, y: cp.y) 378 | } 379 | case 2: 380 | if dir == .left { 381 | startPoint = CGPoint(x: contView.frame.midX + contView.frame.midX / 4, y: contView.frame.minY - 10) 382 | endPoint = CGPoint(x: helpView.frame.minX + 5, y: helpView.frame.maxY + offsetTop) 383 | 384 | let cp = calcCenterPoint(startPoint, end: endPoint) 385 | controlPoint = CGPoint(x: cp.x - 50, y: cp.y) 386 | } else { 387 | startPoint = CGPoint(x: contView.frame.midX + contView.frame.midX / 4, y: contView.frame.minY - 10) 388 | endPoint = CGPoint(x: helpView.frame.midX + 5, y: helpView.frame.maxY + offsetTop) 389 | 390 | let cp = calcCenterPoint(startPoint, end: endPoint) 391 | controlPoint = CGPoint(x: cp.x + 50, y: cp.y) 392 | } 393 | case 3: 394 | if dir == .left { 395 | startPoint = CGPoint(x: contView.frame.midX - contView.frame.midX / 4, y: contView.frame.maxY + 10) 396 | endPoint = CGPoint(x: helpView.frame.midX, y: helpView.frame.minY + offsetBottom) 397 | 398 | let cp = calcCenterPoint(startPoint, end: endPoint) 399 | controlPoint = CGPoint(x: cp.x - 50, y: cp.y) 400 | } else { 401 | startPoint = CGPoint(x: contView.frame.midX - contView.frame.midX / 4, y: contView.frame.maxY + 10) 402 | endPoint = CGPoint(x: helpView.frame.maxX, y: helpView.frame.minY + offsetBottom) 403 | 404 | let cp = calcCenterPoint(startPoint, end: endPoint) 405 | controlPoint = CGPoint(x: cp.x + 50, y: cp.y) 406 | } 407 | case 4: 408 | if dir == .left { 409 | startPoint = CGPoint(x: contView.frame.maxX - 50, y: contView.frame.maxY + 8) 410 | endPoint = CGPoint(x: helpView.frame.maxX - 50, y: helpView.frame.minY + offsetBottom) 411 | 412 | let cp = calcCenterPoint(startPoint, end: endPoint) 413 | controlPoint = CGPoint(x: cp.x + 50, y: cp.y) 414 | } else { 415 | startPoint = CGPoint(x: contView.frame.midX, y: contView.frame.maxY + 10) 416 | endPoint = CGPoint(x: helpView.frame.midX - 25, y: helpView.frame.minY + offsetBottom) 417 | 418 | let cp = calcCenterPoint(startPoint, end: endPoint) 419 | controlPoint = CGPoint(x: cp.x - 50, y: cp.y) 420 | } 421 | default: 422 | break 423 | } 424 | let lineShape: CAShapeLayer! 425 | var bubbleShape: CAShapeLayer? 426 | 427 | switch _lineType { 428 | case .dash_bubble: 429 | lineShape = drawLine(startPoint: startPoint, endPoint: endPoint, controlPoint: controlPoint) 430 | lineShape.lineDashPattern = [3, 6] 431 | bubbleShape = drawHead(endPoint) 432 | case .line_arrow: 433 | lineShape = drawArrow(startPoint: startPoint, endPoint: endPoint, controlPoint: controlPoint) 434 | lineShape.lineDashPattern = nil 435 | case .line_bubble: 436 | lineShape = drawLine(startPoint: startPoint, endPoint: endPoint, controlPoint: controlPoint) 437 | lineShape.lineDashPattern = nil 438 | bubbleShape = drawHead(endPoint) 439 | } 440 | 441 | self.backgroundView.layer.addSublayer(lineShape) 442 | if let bs = bubbleShape{ 443 | self.backgroundView.layer.addSublayer(bs) 444 | } 445 | animateArrow(lineShape) 446 | } 447 | 448 | fileprivate func setSection(_ targetPoint: CGPoint) -> Int { 449 | guard let topView = topView else { return 0 } 450 | 451 | let centerPoint: CGPoint = topView.center 452 | if targetPoint == centerPoint { 453 | return 0 454 | } else if targetPoint.x <= centerPoint.x && targetPoint.y < centerPoint.y { 455 | return 1 456 | } else if targetPoint.x < centerPoint.x && targetPoint.y > centerPoint.y { 457 | return 3 458 | } else if targetPoint.x > centerPoint.x && targetPoint.y < centerPoint.y { 459 | return 2 460 | } else if targetPoint.x >= centerPoint.x && targetPoint.y > centerPoint.y { 461 | return 4 462 | } else { 463 | return 1 464 | } 465 | } 466 | 467 | fileprivate func setSectionPoint(_ section: Int) -> [NSLayoutConstraint] { 468 | guard let topView = topView else { return [] } 469 | 470 | let dynamicSpace = CGFloat(arc4random_uniform(20) + 100) 471 | switch section { 472 | case 0, 1, 2: 473 | let x = contView.centerXAnchor.constraint(equalTo: topView.centerXAnchor, constant: 0.0) 474 | x.isActive = true 475 | let y = contView.topAnchor.constraint(equalTo: helpView.bottomAnchor, constant: dynamicSpace) 476 | y.isActive = true 477 | 478 | let right = contView.rightAnchor.constraint(equalTo: rightAnchor, constant: -16) 479 | right.isActive = true 480 | let left = contView.leftAnchor.constraint(equalTo: leftAnchor, constant: 16) 481 | left.isActive = true 482 | 483 | return [x, y, left, right] 484 | case 3, 4: 485 | let x = contView.centerXAnchor.constraint(equalTo: topView.centerXAnchor, constant: 0.0) 486 | x.isActive = true 487 | let y = contView.bottomAnchor.constraint(equalTo: helpView.topAnchor, constant: -dynamicSpace) 488 | y.isActive = true 489 | 490 | let right = contView.rightAnchor.constraint(equalTo: rightAnchor, constant: -16) 491 | right.isActive = true 492 | let left = contView.leftAnchor.constraint(equalTo: leftAnchor, constant: 16) 493 | left.isActive = true 494 | 495 | return [x, y, left, right] 496 | default: 497 | return [] 498 | } 499 | } 500 | } 501 | 502 | //MARK: - Drawing lines 503 | extension GDOverlay { 504 | fileprivate func drawArrow(startPoint: CGPoint, endPoint: CGPoint, controlPoint: CGPoint) -> CAShapeLayer { 505 | let shapeLayer = CAShapeLayer() 506 | shapeLayer.fillColor = nil 507 | shapeLayer.strokeColor = _arrowColor.cgColor 508 | shapeLayer.lineWidth = _arrowWidth 509 | shapeLayer.lineJoin = CAShapeLayerLineJoin.round 510 | shapeLayer.lineCap = CAShapeLayerLineCap.round 511 | 512 | let path = UIBezierPath() 513 | path.addArrowForm(point: endPoint, controlPoint: controlPoint, width: 5, height: 10) 514 | path.addQuadCurve(to: startPoint, controlPoint: controlPoint) 515 | shapeLayer.path = path.cgPath 516 | 517 | return shapeLayer 518 | } 519 | 520 | fileprivate func drawLine(startPoint: CGPoint, endPoint: CGPoint, controlPoint: CGPoint) -> CAShapeLayer { 521 | let bez = UIBezierPath() 522 | bez.move(to: CGPoint(x: startPoint.x, y: startPoint.y)) 523 | bez.addQuadCurve(to: CGPoint(x: endPoint.x, y: endPoint.y), controlPoint: controlPoint) 524 | 525 | let shape = CAShapeLayer() 526 | shape.path = bez.cgPath 527 | shape.strokeColor = _arrowColor.cgColor 528 | shape.fillColor = nil 529 | shape.lineWidth = _arrowWidth 530 | shape.lineCap = CAShapeLayerLineCap.round 531 | shape.lineJoin = CAShapeLayerLineJoin.miter 532 | shape.strokeStart = 0.0 533 | shape.strokeEnd = 0.0 534 | 535 | return shape 536 | } 537 | 538 | fileprivate func drawHead(_ endPoint: CGPoint) -> CAShapeLayer { 539 | let circlePath: UIBezierPath = UIBezierPath(arcCenter: CGPoint(x: endPoint.x, y: endPoint.y), radius: _headRadius, startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true) 540 | let circleShape = CAShapeLayer() 541 | circleShape.path = circlePath.cgPath 542 | circleShape.fillColor = _headColor.cgColor 543 | 544 | return circleShape 545 | } 546 | 547 | fileprivate func animateArrow(_ shape1: CAShapeLayer) { 548 | let arrowAnim = CABasicAnimation(keyPath: "strokeEnd") 549 | arrowAnim.fromValue = 0.0 550 | arrowAnim.toValue = 1.0 551 | arrowAnim.duration = 0.5 552 | arrowAnim.autoreverses = false 553 | arrowAnim.fillMode = CAMediaTimingFillMode.forwards 554 | arrowAnim.isRemovedOnCompletion = false 555 | 556 | shape1.add(arrowAnim, forKey: nil) 557 | } 558 | } 559 | 560 | -------------------------------------------------------------------------------- /Sources/SwiftyOverlay/Utilities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utilities.swift 3 | // SwiftyGuideOverlay 4 | // 5 | // Created by Saeid Basirnia on 1/16/18. 6 | // Copyright © 2018 Saeid Basirnia. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum LineDirection: UInt32 { 12 | case left 13 | case right 14 | 15 | static let _count: LineDirection.RawValue = { 16 | var maxValue: UInt32 = 0 17 | while let _ = LineDirection(rawValue: maxValue) { 18 | maxValue += 1 19 | } 20 | return maxValue 21 | }() 22 | 23 | static var randomDir: LineDirection { 24 | let rand = arc4random_uniform(2) 25 | return LineDirection(rawValue: rand) ?? .left 26 | } 27 | } 28 | 29 | public enum LineType{ 30 | case line_arrow 31 | case line_bubble 32 | case dash_bubble 33 | } 34 | 35 | // MARK: - helpers 36 | extension GDOverlay { 37 | private var keyWindow: UIWindow? { 38 | var keyWindow: UIWindow? 39 | if #available(iOS 13.0, *) { 40 | keyWindow = UIApplication.shared.connectedScenes 41 | .filter { $0.activationState == .foregroundActive } 42 | .map { $0 as? UIWindowScene } 43 | .compactMap { $0 } 44 | .first?.windows 45 | .filter { $0.isKeyWindow }.first 46 | } else { 47 | keyWindow = UIApplication.shared.keyWindow 48 | } 49 | return keyWindow 50 | } 51 | 52 | private var statusBarHeight: CGFloat { 53 | if #available(iOS 13.0, *) { 54 | return keyWindow?.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0 55 | } else { 56 | return UIApplication.shared.statusBarFrame.height 57 | } 58 | } 59 | 60 | var topView: UIView? { 61 | return keyWindow 62 | } 63 | 64 | func calculateNavHeight(_ vc: UIViewController) -> CGFloat { 65 | guard let navigationController = vc.navigationController else { return 0 } 66 | return navigationController.navigationBar.frame.height + statusBarHeight 67 | } 68 | } 69 | 70 | extension CGRect { 71 | var center: CGPoint { 72 | return CGPoint(x: midX, y: midY) 73 | } 74 | } 75 | 76 | extension UIBezierPath { 77 | func addArrowForm(point: CGPoint, controlPoint: CGPoint, width: CGFloat, height: CGFloat){ 78 | let angle: CGFloat = CGFloat(atan2f(Float(point.y - controlPoint.y), Float(point.x - controlPoint.x))) 79 | let angleAdjustment: CGFloat = CGFloat(atan2f(Float(width), Float(-height))) 80 | let distance: CGFloat = CGFloat(hypotf(Float(width), Float(height))) 81 | 82 | move(to: point) 83 | addLine(to: calculatePointFromPoint(point: point, angle: angle + angleAdjustment, distance: distance)) 84 | addLine(to: point) 85 | addLine(to: calculatePointFromPoint(point: point, angle: angle - angleAdjustment, distance: distance)) 86 | addLine(to: point) 87 | } 88 | 89 | private func calculatePointFromPoint(point: CGPoint, angle: CGFloat, distance: CGFloat) -> CGPoint { 90 | return CGPoint(x: CGFloat(Float(point.x) + cosf(Float(angle)) * Float(distance)), y: CGFloat(Float(point.y) + sinf(Float(angle)) * Float(distance))) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /SwiftyOverlay.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SwiftyOverlay" 3 | s.version = "1.1.6" 4 | s.summary = "App showcase and runtime tour" 5 | s.homepage = "https://github.com/saeid/SwiftyOverlay" 6 | s.license = 'MIT' 7 | s.author = { "Saeid Basirnia" => "saeid.basirniaa@gmail.com" } 8 | s.source = { :git => "https://github.com/saeid/SwiftyOverlay.git", :tag => "1.1.6"} 9 | 10 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '5.0' } 11 | s.platform = :ios 12 | s.ios.deployment_target = '9.0' 13 | s.requires_arc = true 14 | s.swift_version = '5.0' 15 | s.source_files = 'SwiftyOverlay/**/*.{swift}' 16 | s.frameworks = 'UIKit' 17 | 18 | end 19 | 20 | 21 | -------------------------------------------------------------------------------- /SwiftyOverlay.podspec~: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SwiftyOverlay" 3 | s.version = "1.1.4" 4 | s.summary = "App showcase and runtime tour" 5 | s.homepage = "https://github.com/saeid/SwiftyOverlay" 6 | s.license = 'MIT' 7 | s.author = { "Saeid Basirnia" => "saeid.basirniaa@gmail.com" } 8 | s.source = { :git => "https://github.com/saeid/SwiftyOverlay.git", :tag => "1.1.4"} 9 | 10 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4.2' } 11 | s.platform = :ios 12 | s.ios.deployment_target = '9.0' 13 | s.requires_arc = true 14 | s.swift_version = '4.2' 15 | s.source_files = 'SwiftyOverlay/**/*.{swift}' 16 | s.frameworks = 'UIKit' 17 | 18 | end 19 | 20 | 21 | -------------------------------------------------------------------------------- /SwiftyOverlay.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 36FD596F200E6C5100374CA7 /* SwiftyOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 36FD596D200E6C5100374CA7 /* SwiftyOverlay.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 36FD5977200E6C5900374CA7 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FD5975200E6C5900374CA7 /* Utilities.swift */; }; 12 | 36FD5978200E6C5900374CA7 /* GDOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FD5976200E6C5900374CA7 /* GDOverlay.swift */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXFileReference section */ 16 | 36FD596A200E6C5100374CA7 /* SwiftyOverlay.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyOverlay.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 17 | 36FD596D200E6C5100374CA7 /* SwiftyOverlay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftyOverlay.h; sourceTree = ""; }; 18 | 36FD596E200E6C5100374CA7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 19 | 36FD5975200E6C5900374CA7 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; 20 | 36FD5976200E6C5900374CA7 /* GDOverlay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDOverlay.swift; sourceTree = ""; }; 21 | /* End PBXFileReference section */ 22 | 23 | /* Begin PBXFrameworksBuildPhase section */ 24 | 36FD5966200E6C5100374CA7 /* Frameworks */ = { 25 | isa = PBXFrameworksBuildPhase; 26 | buildActionMask = 2147483647; 27 | files = ( 28 | ); 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXFrameworksBuildPhase section */ 32 | 33 | /* Begin PBXGroup section */ 34 | 36FD5960200E6C5100374CA7 = { 35 | isa = PBXGroup; 36 | children = ( 37 | 36FD596C200E6C5100374CA7 /* SwiftyOverlay */, 38 | 36FD596B200E6C5100374CA7 /* Products */, 39 | ); 40 | sourceTree = ""; 41 | }; 42 | 36FD596B200E6C5100374CA7 /* Products */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | 36FD596A200E6C5100374CA7 /* SwiftyOverlay.framework */, 46 | ); 47 | name = Products; 48 | sourceTree = ""; 49 | }; 50 | 36FD596C200E6C5100374CA7 /* SwiftyOverlay */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 36FD5976200E6C5900374CA7 /* GDOverlay.swift */, 54 | 36FD5975200E6C5900374CA7 /* Utilities.swift */, 55 | 36FD596D200E6C5100374CA7 /* SwiftyOverlay.h */, 56 | 36FD596E200E6C5100374CA7 /* Info.plist */, 57 | ); 58 | path = SwiftyOverlay; 59 | sourceTree = ""; 60 | }; 61 | /* End PBXGroup section */ 62 | 63 | /* Begin PBXHeadersBuildPhase section */ 64 | 36FD5967200E6C5100374CA7 /* Headers */ = { 65 | isa = PBXHeadersBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 36FD596F200E6C5100374CA7 /* SwiftyOverlay.h in Headers */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXHeadersBuildPhase section */ 73 | 74 | /* Begin PBXNativeTarget section */ 75 | 36FD5969200E6C5100374CA7 /* SwiftyOverlay */ = { 76 | isa = PBXNativeTarget; 77 | buildConfigurationList = 36FD5972200E6C5100374CA7 /* Build configuration list for PBXNativeTarget "SwiftyOverlay" */; 78 | buildPhases = ( 79 | 36FD5965200E6C5100374CA7 /* Sources */, 80 | 36FD5966200E6C5100374CA7 /* Frameworks */, 81 | 36FD5967200E6C5100374CA7 /* Headers */, 82 | 36FD5968200E6C5100374CA7 /* Resources */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = SwiftyOverlay; 89 | productName = SwiftyOverlay; 90 | productReference = 36FD596A200E6C5100374CA7 /* SwiftyOverlay.framework */; 91 | productType = "com.apple.product-type.framework"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | 36FD5961200E6C5100374CA7 /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastUpgradeCheck = 0930; 100 | ORGANIZATIONNAME = "Saeid Basirnia"; 101 | TargetAttributes = { 102 | 36FD5969200E6C5100374CA7 = { 103 | CreatedOnToolsVersion = 9.2; 104 | LastSwiftMigration = 1020; 105 | ProvisioningStyle = Automatic; 106 | }; 107 | }; 108 | }; 109 | buildConfigurationList = 36FD5964200E6C5100374CA7 /* Build configuration list for PBXProject "SwiftyOverlay" */; 110 | compatibilityVersion = "Xcode 8.0"; 111 | developmentRegion = en; 112 | hasScannedForEncodings = 0; 113 | knownRegions = ( 114 | en, 115 | Base, 116 | ); 117 | mainGroup = 36FD5960200E6C5100374CA7; 118 | productRefGroup = 36FD596B200E6C5100374CA7 /* Products */; 119 | projectDirPath = ""; 120 | projectRoot = ""; 121 | targets = ( 122 | 36FD5969200E6C5100374CA7 /* SwiftyOverlay */, 123 | ); 124 | }; 125 | /* End PBXProject section */ 126 | 127 | /* Begin PBXResourcesBuildPhase section */ 128 | 36FD5968200E6C5100374CA7 /* Resources */ = { 129 | isa = PBXResourcesBuildPhase; 130 | buildActionMask = 2147483647; 131 | files = ( 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXResourcesBuildPhase section */ 136 | 137 | /* Begin PBXSourcesBuildPhase section */ 138 | 36FD5965200E6C5100374CA7 /* Sources */ = { 139 | isa = PBXSourcesBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | 36FD5977200E6C5900374CA7 /* Utilities.swift in Sources */, 143 | 36FD5978200E6C5900374CA7 /* GDOverlay.swift in Sources */, 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | /* End PBXSourcesBuildPhase section */ 148 | 149 | /* Begin XCBuildConfiguration section */ 150 | 36FD5970200E6C5100374CA7 /* Debug */ = { 151 | isa = XCBuildConfiguration; 152 | buildSettings = { 153 | ALWAYS_SEARCH_USER_PATHS = NO; 154 | CLANG_ANALYZER_NONNULL = YES; 155 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 156 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 157 | CLANG_CXX_LIBRARY = "libc++"; 158 | CLANG_ENABLE_MODULES = YES; 159 | CLANG_ENABLE_OBJC_ARC = YES; 160 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 161 | CLANG_WARN_BOOL_CONVERSION = YES; 162 | CLANG_WARN_COMMA = YES; 163 | CLANG_WARN_CONSTANT_CONVERSION = YES; 164 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 165 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 166 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 167 | CLANG_WARN_EMPTY_BODY = YES; 168 | CLANG_WARN_ENUM_CONVERSION = YES; 169 | CLANG_WARN_INFINITE_RECURSION = YES; 170 | CLANG_WARN_INT_CONVERSION = YES; 171 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 172 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 173 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 174 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 175 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 176 | CLANG_WARN_STRICT_PROTOTYPES = YES; 177 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 178 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 179 | CLANG_WARN_UNREACHABLE_CODE = YES; 180 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 181 | CODE_SIGN_IDENTITY = "iPhone Developer"; 182 | COPY_PHASE_STRIP = NO; 183 | CURRENT_PROJECT_VERSION = 1; 184 | DEBUG_INFORMATION_FORMAT = dwarf; 185 | ENABLE_STRICT_OBJC_MSGSEND = YES; 186 | ENABLE_TESTABILITY = YES; 187 | GCC_C_LANGUAGE_STANDARD = gnu11; 188 | GCC_DYNAMIC_NO_PIC = NO; 189 | GCC_NO_COMMON_BLOCKS = YES; 190 | GCC_OPTIMIZATION_LEVEL = 0; 191 | GCC_PREPROCESSOR_DEFINITIONS = ( 192 | "DEBUG=1", 193 | "$(inherited)", 194 | ); 195 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 196 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 197 | GCC_WARN_UNDECLARED_SELECTOR = YES; 198 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 199 | GCC_WARN_UNUSED_FUNCTION = YES; 200 | GCC_WARN_UNUSED_VARIABLE = YES; 201 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 202 | MTL_ENABLE_DEBUG_INFO = YES; 203 | ONLY_ACTIVE_ARCH = YES; 204 | SDKROOT = iphoneos; 205 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 206 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 207 | SWIFT_VERSION = 5.0; 208 | VERSIONING_SYSTEM = "apple-generic"; 209 | VERSION_INFO_PREFIX = ""; 210 | }; 211 | name = Debug; 212 | }; 213 | 36FD5971200E6C5100374CA7 /* Release */ = { 214 | isa = XCBuildConfiguration; 215 | buildSettings = { 216 | ALWAYS_SEARCH_USER_PATHS = NO; 217 | CLANG_ANALYZER_NONNULL = YES; 218 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 219 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 220 | CLANG_CXX_LIBRARY = "libc++"; 221 | CLANG_ENABLE_MODULES = YES; 222 | CLANG_ENABLE_OBJC_ARC = YES; 223 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 224 | CLANG_WARN_BOOL_CONVERSION = YES; 225 | CLANG_WARN_COMMA = YES; 226 | CLANG_WARN_CONSTANT_CONVERSION = YES; 227 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 230 | CLANG_WARN_EMPTY_BODY = YES; 231 | CLANG_WARN_ENUM_CONVERSION = YES; 232 | CLANG_WARN_INFINITE_RECURSION = YES; 233 | CLANG_WARN_INT_CONVERSION = YES; 234 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 236 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 237 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 238 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 239 | CLANG_WARN_STRICT_PROTOTYPES = YES; 240 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 241 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 242 | CLANG_WARN_UNREACHABLE_CODE = YES; 243 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 244 | CODE_SIGN_IDENTITY = "iPhone Developer"; 245 | COPY_PHASE_STRIP = NO; 246 | CURRENT_PROJECT_VERSION = 1; 247 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 248 | ENABLE_NS_ASSERTIONS = NO; 249 | ENABLE_STRICT_OBJC_MSGSEND = YES; 250 | GCC_C_LANGUAGE_STANDARD = gnu11; 251 | GCC_NO_COMMON_BLOCKS = YES; 252 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 253 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 254 | GCC_WARN_UNDECLARED_SELECTOR = YES; 255 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 256 | GCC_WARN_UNUSED_FUNCTION = YES; 257 | GCC_WARN_UNUSED_VARIABLE = YES; 258 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 259 | MTL_ENABLE_DEBUG_INFO = NO; 260 | SDKROOT = iphoneos; 261 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 262 | SWIFT_VERSION = 5.0; 263 | VALIDATE_PRODUCT = YES; 264 | VERSIONING_SYSTEM = "apple-generic"; 265 | VERSION_INFO_PREFIX = ""; 266 | }; 267 | name = Release; 268 | }; 269 | 36FD5973200E6C5100374CA7 /* Debug */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | CLANG_ENABLE_MODULES = YES; 273 | CODE_SIGN_IDENTITY = ""; 274 | CODE_SIGN_STYLE = Automatic; 275 | DEFINES_MODULE = YES; 276 | DEVELOPMENT_TEAM = 54N8PNS7FS; 277 | DYLIB_COMPATIBILITY_VERSION = 1; 278 | DYLIB_CURRENT_VERSION = 1; 279 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 280 | INFOPLIST_FILE = SwiftyOverlay/Info.plist; 281 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 282 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 283 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 284 | PRODUCT_BUNDLE_IDENTIFIER = com.saeidbsn.SwiftyOverlay; 285 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 286 | SKIP_INSTALL = YES; 287 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 288 | SWIFT_VERSION = 5.0; 289 | TARGETED_DEVICE_FAMILY = "1,2"; 290 | }; 291 | name = Debug; 292 | }; 293 | 36FD5974200E6C5100374CA7 /* Release */ = { 294 | isa = XCBuildConfiguration; 295 | buildSettings = { 296 | CLANG_ENABLE_MODULES = YES; 297 | CODE_SIGN_IDENTITY = ""; 298 | CODE_SIGN_STYLE = Automatic; 299 | DEFINES_MODULE = YES; 300 | DEVELOPMENT_TEAM = 54N8PNS7FS; 301 | DYLIB_COMPATIBILITY_VERSION = 1; 302 | DYLIB_CURRENT_VERSION = 1; 303 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 304 | INFOPLIST_FILE = SwiftyOverlay/Info.plist; 305 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 306 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 307 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 308 | PRODUCT_BUNDLE_IDENTIFIER = com.saeidbsn.SwiftyOverlay; 309 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 310 | SKIP_INSTALL = YES; 311 | SWIFT_VERSION = 5.0; 312 | TARGETED_DEVICE_FAMILY = "1,2"; 313 | }; 314 | name = Release; 315 | }; 316 | /* End XCBuildConfiguration section */ 317 | 318 | /* Begin XCConfigurationList section */ 319 | 36FD5964200E6C5100374CA7 /* Build configuration list for PBXProject "SwiftyOverlay" */ = { 320 | isa = XCConfigurationList; 321 | buildConfigurations = ( 322 | 36FD5970200E6C5100374CA7 /* Debug */, 323 | 36FD5971200E6C5100374CA7 /* Release */, 324 | ); 325 | defaultConfigurationIsVisible = 0; 326 | defaultConfigurationName = Release; 327 | }; 328 | 36FD5972200E6C5100374CA7 /* Build configuration list for PBXNativeTarget "SwiftyOverlay" */ = { 329 | isa = XCConfigurationList; 330 | buildConfigurations = ( 331 | 36FD5973200E6C5100374CA7 /* Debug */, 332 | 36FD5974200E6C5100374CA7 /* Release */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | /* End XCConfigurationList section */ 338 | }; 339 | rootObject = 36FD5961200E6C5100374CA7 /* Project object */; 340 | } 341 | -------------------------------------------------------------------------------- /SwiftyOverlay.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftyOverlay.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftyOverlay.xcodeproj/project.xcworkspace/xcuserdata/saeid.basirnia.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeid/SwiftyOverlay/e38874af1ecd58e498736e2bebfc7aebaf8ecf68/SwiftyOverlay.xcodeproj/project.xcworkspace/xcuserdata/saeid.basirnia.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SwiftyOverlay.xcodeproj/xcuserdata/saeid.basirnia.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftyOverlay.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SwiftyOverlay.xcodeproj/xcuserdata/saeid.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftyOverlay.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | SwiftyOverlay.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Tests/SwiftyOverlayTests/SwiftyOverlayTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import SwiftyOverlay 3 | 4 | final class SwiftyOverlayTests: XCTestCase { 5 | func testExample() { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | XCTAssertEqual(SwiftyOverlay().text, "Hello, World!") 10 | } 11 | } 12 | --------------------------------------------------------------------------------