├── .gitignore
├── .slather.yml
├── .swift-version
├── .travis.yml
├── Demo
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── about.imageset
│ │ ├── Contents.json
│ │ ├── about-1.png
│ │ └── about.png
│ ├── activityLog.imageset
│ │ ├── Contents.json
│ │ ├── activityLog-1.png
│ │ └── activityLog.png
│ ├── avatar.imageset
│ │ ├── Contents.json
│ │ ├── flyinghd-1.jpg
│ │ └── flyinghd.jpg
│ ├── banner.imageset
│ │ ├── Contents.json
│ │ ├── banner-1.jpg
│ │ └── banner.jpg
│ ├── camera.imageset
│ │ ├── Contents.json
│ │ ├── camera-1.png
│ │ └── camera.png
│ ├── friends.imageset
│ │ ├── Contents.json
│ │ ├── friends-1.png
│ │ └── friends.png
│ ├── more.imageset
│ │ ├── Contents.json
│ │ ├── more-1.png
│ │ └── more.png
│ ├── photos.imageset
│ │ ├── Contents.json
│ │ ├── photos-1.jpg
│ │ └── photos.jpg
│ ├── post.imageset
│ │ ├── Contents.json
│ │ ├── post-1.png
│ │ └── post.png
│ ├── searchBar.imageset
│ │ ├── Contents.json
│ │ ├── searchBar-1.png
│ │ └── searchBar.png
│ └── updateInfo.imageset
│ │ ├── Contents.json
│ │ ├── updateInfo-1.png
│ │ └── updateInfo.png
├── Base.lproj
│ └── LaunchScreen.storyboard
├── FacebookProfileExampleViewController.swift
├── IconButton.swift
├── ImageContainerView.swift
├── Info.plist
├── TestViewController.swift
└── ViewController.swift
├── LICENSE.txt
├── Neon.podspec
├── Neon.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ ├── Neon Demo.xcscheme
│ ├── Neon OSX.xcscheme
│ ├── Neon iOS.xcscheme
│ └── NeonUnitTests.xcscheme
├── NeonUnitTests
├── Info.plist
└── NeonUnitTests.swift
├── README.md
├── Screenshots
├── align.png
├── align_between_fill.png
├── align_fill.png
├── align_offset.png
├── auto_height_1.png
├── auto_height_2.png
├── center.png
├── corner.png
├── demo.gif
├── edge.png
├── fill.png
├── fill_edge.png
├── group_against_edge.png
├── group_and_fill.png
├── group_in_center.png
├── group_in_corner.png
├── group_relative.png
├── landscape.png
├── logo.png
├── portrait.png
└── side_by_side.png
└── Source
├── Info.plist
├── Neon.h
├── Neon.swift
├── NeonAlignable.swift
├── NeonAnchorable.swift
├── NeonExtensions.swift
├── NeonFrameable.swift
└── NeonGroupable.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 |
30 | # CocoaPods
31 | #
32 | # We recommend against adding the Pods directory to your .gitignore. However
33 | # you should judge for yourself, the pros and cons are mentioned at:
34 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
35 | #
36 | # Pods/
37 |
38 | # Carthage
39 | #
40 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
41 | # Carthage/Checkouts
42 |
43 | Carthage/Build
44 |
--------------------------------------------------------------------------------
/.slather.yml:
--------------------------------------------------------------------------------
1 | coverage_service: coveralls
2 | xcodeproj: Neon.xcodeproj
3 | scheme: NeonUnitTests
4 | source_directory: Source
5 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: swift
2 | osx_image: xcode8
3 | before_install: gem install cocoapods xcpretty obcd slather -N
4 | xcode_sdk: iphonesimulator10.0
5 | xcode_project: Neon.xcodeproj
6 | xcode_scheme: NeonUnitTests
7 | after_success: slather
8 |
--------------------------------------------------------------------------------
/Demo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Neon
4 | //
5 | // Created by Mike on 9/16/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 | var window: UIWindow?
14 |
15 | func applicationDidFinishLaunching(_ application: UIApplication) {
16 | self.window = UIWindow.init(frame: UIScreen.main.bounds)
17 | self.window?.backgroundColor = UIColor.white
18 |
19 | // The facebook example from the README.... Don't ask why the file name is FacebookProfileExampleViewController
20 | // but the class referenced here is TwitterProfileExampleViewController... I didn't realize you can't yet refactor
21 | // swift code, and somehow this still works...
22 | UINavigationBar.appearance().barTintColor = UIColor(red: 80/255.0, green: 108/255.0, blue: 163/255.0, alpha: 1.0)
23 | UINavigationBar.appearance().isTranslucent = false
24 | // self.window?.rootViewController = UINavigationController(rootViewController: TwitterProfileExampleViewController())
25 |
26 | // Used for tinkering and testing new methods - uncomment this out if you want to experiment!
27 | // self.window?.rootViewController = TestViewController()
28 |
29 | // This is crazy-complex demo I have on the README. Uncomment this out to experience with that as well,
30 | // although I really built it for iPads.
31 | self.window?.rootViewController = ViewController()
32 |
33 | self.window?.makeKeyAndVisible()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/about.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "about-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "about.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/about.imageset/about-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/about.imageset/about-1.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/about.imageset/about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/about.imageset/about.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/activityLog.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "activityLog-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "activityLog.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/activityLog.imageset/activityLog-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/activityLog.imageset/activityLog-1.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/activityLog.imageset/activityLog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/activityLog.imageset/activityLog.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/avatar.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "flyinghd-1.jpg",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "flyinghd.jpg",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/avatar.imageset/flyinghd-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/avatar.imageset/flyinghd-1.jpg
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/avatar.imageset/flyinghd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/avatar.imageset/flyinghd.jpg
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/banner.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "banner-1.jpg",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "banner.jpg",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/banner.imageset/banner-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/banner.imageset/banner-1.jpg
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/banner.imageset/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/banner.imageset/banner.jpg
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/camera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "camera-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "camera.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/camera.imageset/camera-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/camera.imageset/camera-1.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/camera.imageset/camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/camera.imageset/camera.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/friends.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "friends-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "friends.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/friends.imageset/friends-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/friends.imageset/friends-1.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/friends.imageset/friends.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/friends.imageset/friends.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/more.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "more-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "more.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/more.imageset/more-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/more.imageset/more-1.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/more.imageset/more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/more.imageset/more.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/photos.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "photos-1.jpg",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "photos.jpg",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/photos.imageset/photos-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/photos.imageset/photos-1.jpg
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/photos.imageset/photos.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/photos.imageset/photos.jpg
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/post.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "post-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "post.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/post.imageset/post-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/post.imageset/post-1.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/post.imageset/post.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/post.imageset/post.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/searchBar.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "searchBar-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "searchBar.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/searchBar.imageset/searchBar-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/searchBar.imageset/searchBar-1.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/searchBar.imageset/searchBar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/searchBar.imageset/searchBar.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/updateInfo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "updateInfo-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "updateInfo.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/updateInfo.imageset/updateInfo-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/updateInfo.imageset/updateInfo-1.png
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/updateInfo.imageset/updateInfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Demo/Assets.xcassets/updateInfo.imageset/updateInfo.png
--------------------------------------------------------------------------------
/Demo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Demo/FacebookProfileExampleViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FacebookProfileExampleViewController.swift
3 | // Neon
4 | //
5 | // Created by Mike on 9/26/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Neon
11 |
12 |
13 | class TwitterProfileExampleViewController: UIViewController {
14 | let searchBar : UIImageView = UIImageView()
15 | let bannerImageView : UIImageView = UIImageView()
16 | let bannerMaskView : UIView = UIView()
17 | let avatarImageView : UIImageView = UIImageView()
18 | let nameLabel : UILabel = UILabel()
19 | let cameraButton : UIButton = UIButton(type: .custom)
20 |
21 | let buttonContainerView : UIView = UIView()
22 | let postButton : IconButton = IconButton()
23 | let updateInfoButton : IconButton = IconButton()
24 | let activityLogButton : IconButton = IconButton()
25 | let moreButton : IconButton = IconButton()
26 |
27 | let buttonContainerView2 : UIView = UIView()
28 | let aboutView : ImageContainerView = ImageContainerView()
29 | let photosView : ImageContainerView = ImageContainerView()
30 | let friendsView : ImageContainerView = ImageContainerView()
31 |
32 | convenience init() {
33 | self.init(nibName:nil, bundle:nil)
34 |
35 | // So I cheated here... shhhh!
36 | // (can't take all damn day making this match perfectly!)
37 | searchBar.image = UIImage(named: "searchBar")
38 | searchBar.contentMode = .scaleAspectFit
39 |
40 | bannerImageView.image = UIImage(named: "banner")
41 | bannerImageView.contentMode = .scaleAspectFill
42 | bannerImageView.clipsToBounds = true
43 |
44 | bannerMaskView.backgroundColor = UIColor(white: 0.0, alpha: 0.2)
45 |
46 | avatarImageView.image = UIImage(named: "avatar")
47 | avatarImageView.layer.cornerRadius = 1.0
48 | avatarImageView.layer.borderColor = UIColor.white.cgColor
49 | avatarImageView.layer.borderWidth = 2.0
50 | avatarImageView.clipsToBounds = true
51 |
52 | cameraButton.setImage(UIImage(named: "camera"), for: .normal)
53 |
54 | nameLabel.textColor = UIColor.white
55 | nameLabel.text = "Mike\nAmaral"
56 | nameLabel.numberOfLines = 2
57 | nameLabel.font = UIFont(name: "HelveticaNeue-Light", size: 33)
58 |
59 | buttonContainerView.backgroundColor = UIColor.white
60 | buttonContainerView.layer.shadowColor = UIColor.black.cgColor
61 | buttonContainerView.layer.shadowOffset = CGSize(width: 0, height: 0.5)
62 | buttonContainerView.layer.shadowOpacity = 0.4
63 |
64 | postButton.label.text = "Post"
65 | postButton.imageView.image = UIImage(named: "post")
66 |
67 | updateInfoButton.label.text = "Update Info"
68 | updateInfoButton.imageView.image = UIImage(named: "updateInfo")
69 |
70 | activityLogButton.label.text = "Activity Log"
71 | activityLogButton.imageView.image = UIImage(named: "activityLog")
72 |
73 | moreButton.label.text = "More"
74 | moreButton.imageView.image = UIImage(named: "more")
75 |
76 | buttonContainerView2.backgroundColor = UIColor.clear
77 |
78 | aboutView.imageView.image = UIImage(named: "about")
79 | aboutView.label.text = "About"
80 |
81 | photosView.imageView.image = UIImage(named: "photos")
82 | photosView.label.text = "Photos"
83 |
84 | friendsView.imageView.image = UIImage(named: "friends")
85 | friendsView.label.text = "Friends"
86 | }
87 |
88 | override func viewDidLoad() {
89 | super.viewDidLoad()
90 |
91 | navigationItem.titleView = searchBar
92 |
93 | view.backgroundColor = UIColor(red: 211/255.0, green: 214/255.0, blue: 219/255.0, alpha: 1.0)
94 |
95 | view.addSubview(bannerImageView)
96 | bannerImageView.addSubview(bannerMaskView)
97 | bannerImageView.addSubview(cameraButton)
98 | bannerImageView.addSubview(avatarImageView)
99 | bannerImageView.addSubview(nameLabel)
100 |
101 | view.addSubview(buttonContainerView)
102 | buttonContainerView.addSubview(postButton)
103 | buttonContainerView.addSubview(updateInfoButton)
104 | buttonContainerView.addSubview(activityLogButton)
105 | buttonContainerView.addSubview(moreButton)
106 |
107 | view.addSubview(buttonContainerView2)
108 | buttonContainerView2.addSubview(aboutView)
109 | buttonContainerView2.addSubview(photosView)
110 | buttonContainerView2.addSubview(friendsView)
111 | }
112 |
113 | override func viewWillLayoutSubviews() {
114 | super.viewWillLayoutSubviews()
115 |
116 | layoutFrames()
117 | }
118 |
119 | func layoutFrames() {
120 | let isLandscape : Bool = UIDevice.current.orientation.isLandscape
121 | let bannerHeight : CGFloat = view.height * 0.465
122 | let avatarHeightMultipler : CGFloat = isLandscape ? 0.75 : 0.43
123 | let avatarSize = bannerHeight * avatarHeightMultipler
124 |
125 | searchBar.fillSuperview()
126 | bannerImageView.anchorAndFillEdge(.top, xPad: 0, yPad: 0, otherSize: bannerHeight)
127 | bannerMaskView.fillSuperview()
128 | avatarImageView.anchorInCorner(.bottomLeft, xPad: 15, yPad: 15, width: avatarSize, height: avatarSize)
129 | nameLabel.alignAndFillWidth(align: .toTheRightCentered, relativeTo: avatarImageView, padding: 15, height: 120)
130 | cameraButton.anchorInCorner(.bottomRight, xPad: 10, yPad: 7, width: 28, height: 28)
131 | buttonContainerView.alignAndFillWidth(align: .underCentered, relativeTo: bannerImageView, padding: 0, height: 62)
132 | buttonContainerView.groupAndFill(group: .horizontal, views: [postButton, updateInfoButton, activityLogButton, moreButton], padding: 10)
133 | buttonContainerView2.alignAndFillWidth(align: .underCentered, relativeTo: buttonContainerView, padding: 0, height: 120)
134 | buttonContainerView2.groupAndFill(group: .horizontal, views: [aboutView, photosView, friendsView], padding: 8)
135 | }
136 | }
137 |
138 |
--------------------------------------------------------------------------------
/Demo/IconButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IconButton.swift
3 | // Neon
4 | //
5 | // Created by Mike on 9/26/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Neon
11 |
12 |
13 | class IconButton: UIView {
14 | let imageView : UIImageView = UIImageView()
15 | let label : UILabel = UILabel()
16 |
17 | convenience init() {
18 | self.init(frame: CGRect.zero)
19 |
20 | imageView.contentMode = .scaleAspectFill
21 | self.addSubview(imageView)
22 |
23 | label.textAlignment = .center
24 | label.textColor = UIColor(red: 106/255.0, green: 113/255.0, blue: 127/255.0, alpha: 1.0)
25 | label.font = UIFont.systemFont(ofSize: 13.0)
26 | self.addSubview(label)
27 | }
28 |
29 | override func layoutSubviews() {
30 | super.layoutSubviews()
31 |
32 | imageView.anchorToEdge(.top, padding: 0, width: 24, height: 24)
33 | label.align(.underCentered, relativeTo: imageView, padding: 5, width: self.width, height: 15)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Demo/ImageContainerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageContainerView.swift
3 | // Neon
4 | //
5 | // Created by Mike on 9/26/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Neon
11 |
12 | class ImageContainerView: UIView {
13 | let imageView : UIImageView = UIImageView()
14 | let label : UILabel = UILabel()
15 |
16 | convenience init() {
17 | self.init(frame: CGRect.zero)
18 |
19 | self.backgroundColor = UIColor.white
20 | self.layer.cornerRadius = 4.0
21 | self.layer.borderWidth = 1.0
22 | self.layer.borderColor = UIColor(white: 0.68, alpha: 1.0).cgColor
23 | self.clipsToBounds = true
24 |
25 | imageView.contentMode = .scaleAspectFill
26 | imageView.clipsToBounds = true
27 | self.addSubview(imageView)
28 |
29 | label.textAlignment = .center
30 | label.textColor = UIColor.black
31 | label.font = UIFont.boldSystemFont(ofSize: 14.0)
32 | self.addSubview(label)
33 | }
34 |
35 | override func layoutSubviews() {
36 | super.layoutSubviews()
37 |
38 | imageView.anchorAndFillEdge(.top, xPad: 0, yPad: 0, otherSize: self.height * 0.7)
39 | label.alignAndFill(align: .underCentered, relativeTo: imageView, padding: 0)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Demo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UIStatusBarStyle
32 | UIStatusBarStyleLightContent
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | UIViewControllerBasedStatusBarAppearance
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Demo/TestViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestViewController.swift
3 | // Neon
4 | //
5 | // Created by Mike on 9/20/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Neon
11 |
12 | class TestViewController: UIViewController {
13 | let anchorLayer : CALayer = CALayer()
14 | let anchorViewA : UILabel = UILabel()
15 | let anchorViewB : UILabel = UILabel()
16 | let anchorViewC : UILabel = UILabel()
17 | let anchorViewD : UILabel = UILabel()
18 | let view1 : UILabel = UILabel()
19 | let view2 : UILabel = UILabel()
20 | let view3 : UILabel = UILabel()
21 | let view4 : UILabel = UILabel()
22 | let view5 : UILabel = UILabel()
23 | let view6 : UILabel = UILabel()
24 | let testLabel : UILabel = UILabel()
25 |
26 | override func viewDidLoad() {
27 | super.viewDidLoad()
28 |
29 | anchorLayer.backgroundColor = UIColor.red.cgColor
30 | view.layer.addSublayer(anchorLayer)
31 |
32 | anchorViewA.backgroundColor = UIColor(red: 229/255.0, green: 72/255.0, blue: 26/255.0, alpha: 1.0)
33 | anchorViewA.text = ""
34 | anchorViewA.textAlignment = .center
35 | anchorViewA.font = UIFont.boldSystemFont(ofSize: 20)
36 | anchorViewA.textColor = UIColor.white
37 | view.addSubview(anchorViewA)
38 |
39 | anchorViewB.backgroundColor = UIColor(red: 229/255.0, green: 72/255.0, blue: 26/255.0, alpha: 1.0)
40 | anchorViewB.text = ""
41 | anchorViewB.textAlignment = .center
42 | anchorViewB.font = UIFont.boldSystemFont(ofSize: 20)
43 | anchorViewB.textColor = UIColor.white
44 | view.addSubview(anchorViewB)
45 |
46 | anchorViewC.backgroundColor = UIColor(red: 229/255.0, green: 72/255.0, blue: 26/255.0, alpha: 1.0)
47 | anchorViewC.text = "C"
48 | anchorViewC.textAlignment = .center
49 | anchorViewC.font = UIFont.boldSystemFont(ofSize: 20)
50 | anchorViewC.textColor = UIColor.white
51 | view.addSubview(anchorViewC)
52 |
53 | anchorViewD.backgroundColor = UIColor(red: 229/255.0, green: 72/255.0, blue: 26/255.0, alpha: 1.0)
54 | anchorViewD.text = "D"
55 | anchorViewD.textAlignment = .center
56 | anchorViewD.font = UIFont.boldSystemFont(ofSize: 20)
57 | anchorViewD.textColor = UIColor.white
58 | view.addSubview(anchorViewD)
59 |
60 | view1.backgroundColor = UIColor(red: 78/255.0, green: 102/255.0, blue: 131/255.0, alpha: 1.0)
61 | view1.text = "1"
62 | view1.textAlignment = .center
63 | view1.font = UIFont.boldSystemFont(ofSize: 20)
64 | view1.textColor = UIColor.white
65 | view.addSubview(view1)
66 |
67 | view2.backgroundColor = UIColor(red: 132/255.0, green: 169/255.0, blue: 57/255.0, alpha: 1.0)
68 | view2.text = "2"
69 | view2.textAlignment = .center
70 | view2.font = UIFont.boldSystemFont(ofSize: 20)
71 | view2.textColor = UIColor.white
72 | view.addSubview(view2)
73 |
74 | view3.backgroundColor = UIColor(red: 78/255.0, green: 102/255.0, blue: 131/255.0, alpha: 1.0)
75 | view3.text = "3"
76 | view3.textAlignment = .center
77 | view3.font = UIFont.boldSystemFont(ofSize: 20)
78 | view3.textColor = UIColor.white
79 | view.addSubview(view3)
80 |
81 | view4.backgroundColor = UIColor(red: 132/255.0, green: 169/255.0, blue: 57/255.0, alpha: 1.0)
82 | view4.text = "4"
83 | view4.textAlignment = .center
84 | view4.font = UIFont.boldSystemFont(ofSize: 20)
85 | view4.textColor = UIColor.white
86 | view.addSubview(view4)
87 |
88 | view5.backgroundColor = UIColor(red: 78/255.0, green: 102/255.0, blue: 131/255.0, alpha: 1.0)
89 | view5.text = "5"
90 | view5.textAlignment = .center
91 | view5.font = UIFont.boldSystemFont(ofSize: 20)
92 | view5.textColor = UIColor.white
93 | view.addSubview(view5)
94 |
95 | view6.backgroundColor = UIColor(red: 132/255.0, green: 169/255.0, blue: 57/255.0, alpha: 1.0)
96 | view6.text = "6"
97 | view6.textAlignment = .center
98 | view6.font = UIFont.boldSystemFont(ofSize: 20)
99 | view6.textColor = UIColor.white
100 | view.addSubview(view6)
101 |
102 | testLabel.backgroundColor = UIColor(red: 132/255.0, green: 169/255.0, blue: 57/255.0, alpha: 1.0)
103 | testLabel.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
104 | testLabel.textAlignment = .center
105 | testLabel.font = UIFont.boldSystemFont(ofSize: 14)
106 | testLabel.textColor = UIColor.white
107 | testLabel.numberOfLines = 0
108 | view.addSubview(testLabel)
109 | }
110 |
111 | override func viewWillLayoutSubviews() {
112 | super.viewWillLayoutSubviews()
113 |
114 | anchorViewA.anchorInCorner(.topLeft, xPad: 20, yPad: 20, width: 200, height: 200)
115 | view1.align(.toTheRightMatchingTop, relativeTo: anchorViewA, padding: 20, width: 100, height: 100, offset: 20)
116 | view2.align(.underMatchingLeft, relativeTo: anchorViewA, padding: 20, width: 100, height: 100, offset: 20)
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Demo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Neon
4 | //
5 | // Created by Mike on 9/16/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Neon
11 |
12 |
13 | class ViewController: UIViewController {
14 | let containerView : UIView = UIView()
15 | let anchorView : UIView = UIView()
16 | let view1 : UILabel = UILabel()
17 | let view2 : UILabel = UILabel()
18 | let view3 : UILabel = UILabel()
19 | let view4 : UILabel = UILabel()
20 | let view5 : UILabel = UILabel()
21 | let view6 : UILabel = UILabel()
22 | let view7 : UILabel = UILabel()
23 | let view8 : UILabel = UILabel()
24 | let view9 : UILabel = UILabel()
25 | let view10 : UILabel = UILabel()
26 | let view11 : UILabel = UILabel()
27 | let view12 : UILabel = UILabel()
28 | let view13 : UILabel = UILabel()
29 | let view14 : UILabel = UILabel()
30 | let view15 : UILabel = UILabel()
31 | let view16 : UILabel = UILabel()
32 | let view17 : UILabel = UILabel()
33 | let view18 : UILabel = UILabel()
34 | let view19 : UILabel = UILabel()
35 | let view20 : UILabel = UILabel()
36 |
37 | override func viewDidLoad() {
38 | super.viewDidLoad()
39 |
40 | containerView.clipsToBounds = true
41 | containerView.backgroundColor = UIColor(red: 61/255.0, green: 61/255.0, blue: 61/255.0, alpha: 1.0)
42 | view.addSubview(containerView)
43 |
44 | anchorView.backgroundColor = UIColor(red: 229/255.0, green: 72/255.0, blue: 26/255.0, alpha: 1.0)
45 | containerView.addSubview(anchorView)
46 |
47 | view1.backgroundColor = UIColor(red: 78/255.0, green: 102/255.0, blue: 131/255.0, alpha: 1.0)
48 | view1.text = "1"
49 | view1.textAlignment = .center
50 | view1.font = UIFont.boldSystemFont(ofSize: 20)
51 | view1.textColor = UIColor.white
52 | containerView.addSubview(view1)
53 |
54 | view2.backgroundColor = UIColor(red: 132/255.0, green: 169/255.0, blue: 57/255.0, alpha: 1.0)
55 | view2.text = "2"
56 | view2.textAlignment = .center
57 | view2.font = UIFont.boldSystemFont(ofSize: 20)
58 | view2.textColor = UIColor.white
59 | containerView.addSubview(view2)
60 |
61 | view3.backgroundColor = UIColor(red: 146/255.0, green: 83/255.0, blue: 72/255.0, alpha: 1.0)
62 | view3.text = "3"
63 | view3.textAlignment = .center
64 | view3.font = UIFont.boldSystemFont(ofSize: 20)
65 | view3.textColor = UIColor.white
66 | containerView.addSubview(view3)
67 |
68 | view4.backgroundColor = UIColor(red: 100/255.0, green: 112/255.0, blue: 108/255.0, alpha: 1.0)
69 | view4.text = "4"
70 | view4.textAlignment = .center
71 | view4.font = UIFont.boldSystemFont(ofSize: 20)
72 | view4.textColor = UIColor.white
73 | containerView.addSubview(view4)
74 |
75 | view5.backgroundColor = UIColor(red: 33/255.0, green: 154/255.0, blue: 209/255.0, alpha: 1.0)
76 | view5.text = "5"
77 | view5.textAlignment = .center
78 | view5.font = UIFont.boldSystemFont(ofSize: 20)
79 | view5.textColor = UIColor.white
80 | containerView.addSubview(view5)
81 |
82 | view6.backgroundColor = UIColor(red: 229/255.0, green: 174/255.0, blue: 84/255.0, alpha: 1.0)
83 | view6.text = "6"
84 | view6.textAlignment = .center
85 | view6.font = UIFont.boldSystemFont(ofSize: 20)
86 | view6.textColor = UIColor.white
87 | containerView.addSubview(view6)
88 |
89 | view7.backgroundColor = UIColor(red: 222/255.0, green: 81/255.0, blue: 62/255.0, alpha: 1.0)
90 | view7.text = "7"
91 | view7.textAlignment = .center
92 | view7.font = UIFont.boldSystemFont(ofSize: 20)
93 | view7.textColor = UIColor.white
94 | containerView.addSubview(view7)
95 |
96 | view8.backgroundColor = UIColor(red: 198/255.0, green: 173/255.0, blue: 138/255.0, alpha: 1.0)
97 | view8.text = "8"
98 | view8.textAlignment = .center
99 | view8.font = UIFont.boldSystemFont(ofSize: 20)
100 | view8.textColor = UIColor.white
101 | containerView.addSubview(view8)
102 |
103 | view9.backgroundColor = UIColor(red: 157/255.0, green: 104/255.0, blue: 80/255.0, alpha: 1.0)
104 | view9.text = "9"
105 | view9.textAlignment = .center
106 | view9.font = UIFont.boldSystemFont(ofSize: 20)
107 | view9.textColor = UIColor.white
108 | containerView.addSubview(view9)
109 |
110 | view10.backgroundColor = UIColor(red: 23/255.0, green: 59/255.0, blue: 140/255.0, alpha: 1.0)
111 | view10.text = "10"
112 | view10.textAlignment = .center
113 | view10.font = UIFont.boldSystemFont(ofSize: 20)
114 | view10.textColor = UIColor.white
115 | containerView.addSubview(view10)
116 |
117 | view11.backgroundColor = UIColor(red: 229/255.0, green: 174/255.0, blue: 84/255.0, alpha: 1.0)
118 | view11.text = "11"
119 | view11.textAlignment = .center
120 | view11.font = UIFont.boldSystemFont(ofSize: 20)
121 | view11.textColor = UIColor.white
122 | anchorView.addSubview(view11)
123 |
124 | view12.backgroundColor = UIColor(red: 33/255.0, green: 154/255.0, blue: 209/255.0, alpha: 1.0)
125 | view12.text = "12"
126 | view12.textAlignment = .center
127 | view12.font = UIFont.boldSystemFont(ofSize: 20)
128 | view12.textColor = UIColor.white
129 | anchorView.addSubview(view12)
130 |
131 | view13.backgroundColor = UIColor(red: 100/255.0, green: 112/255.0, blue: 108/255.0, alpha: 1.0)
132 | view13.text = "13"
133 | view13.textAlignment = .center
134 | view13.font = UIFont.boldSystemFont(ofSize: 20)
135 | view13.textColor = UIColor.white
136 | anchorView.addSubview(view13)
137 |
138 | view14.backgroundColor = UIColor(red: 198/255.0, green: 173/255.0, blue: 138/255.0, alpha: 1.0)
139 | view14.text = "14"
140 | view14.textAlignment = .center
141 | view14.font = UIFont.boldSystemFont(ofSize: 20)
142 | view14.textColor = UIColor.white
143 | containerView.addSubview(view14)
144 |
145 | view15.backgroundColor = UIColor(red: 146/255.0, green: 83/255.0, blue: 72/255.0, alpha: 1.0)
146 | view15.text = "15"
147 | view15.textAlignment = .center
148 | view15.font = UIFont.boldSystemFont(ofSize: 20)
149 | view15.textColor = UIColor.white
150 | containerView.addSubview(view15)
151 |
152 | view16.backgroundColor = UIColor(red: 78/255.0, green: 102/255.0, blue: 131/255.0, alpha: 1.0)
153 | view16.text = "16"
154 | view16.textAlignment = .center
155 | view16.font = UIFont.boldSystemFont(ofSize: 20)
156 | view16.textColor = UIColor.white
157 | containerView.addSubview(view16)
158 |
159 | view17.backgroundColor = UIColor(red: 33/255.0, green: 154/255.0, blue: 209/255.0, alpha: 1.0)
160 | view17.text = "17"
161 | view17.textAlignment = .center
162 | view17.font = UIFont.boldSystemFont(ofSize: 20)
163 | view17.textColor = UIColor.white
164 | containerView.addSubview(view17)
165 |
166 | view18.backgroundColor = UIColor(red: 146/255.0, green: 83/255.0, blue: 72/255.0, alpha: 1.0)
167 | view18.text = "18"
168 | view18.textAlignment = .center
169 | view18.font = UIFont.boldSystemFont(ofSize: 20)
170 | view18.textColor = UIColor.white
171 | containerView.addSubview(view18)
172 |
173 | view19.backgroundColor = UIColor(red: 23/255.0, green: 59/255.0, blue: 140/255.0, alpha: 1.0)
174 | view19.text = "19"
175 | view19.textAlignment = .center
176 | view19.font = UIFont.boldSystemFont(ofSize: 20)
177 | view19.textColor = UIColor.white
178 | containerView.addSubview(view19)
179 |
180 | view20.backgroundColor = UIColor(red: 222/255.0, green: 81/255.0, blue: 62/255.0, alpha: 1.0)
181 | view20.text = "20"
182 | view20.textAlignment = .center
183 | view20.font = UIFont.boldSystemFont(ofSize: 20)
184 | view20.textColor = UIColor.white
185 | containerView.addSubview(view20)
186 | }
187 |
188 | override func viewWillLayoutSubviews() {
189 | super.viewWillLayoutSubviews()
190 |
191 | // Static container for the demo.
192 | //
193 | containerView.fillSuperview(left: 10, right: 10, top: 25, bottom: 10)
194 | anchorView.anchorInCenter(width: 200, height: 200)
195 |
196 | layoutFrames()
197 | }
198 |
199 | func layoutFrames() {
200 | anchorView.groupInCorner(group: .vertical, views: [view11, view12, view13], inCorner: .topRight, padding: 10, width: 40, height: 40)
201 | view1.alignAndFillWidth(align: .toTheRightMatchingTop, relativeTo: anchorView, padding: 10, height: 50)
202 | containerView.groupAndAlign(group: .horizontal, andAlign: .underMatchingLeft, views: [view2, view3, view4], relativeTo: view1, padding: 10, width: 60, height: 60)
203 | view5.alignAndFillWidth(align: .toTheRightMatchingTop, relativeTo: view4, padding: 10, height: 60)
204 | view6.alignAndFill(align: .underMatchingLeft, relativeTo: view2, padding: 10)
205 | view7.alignAndFillHeight(align: .aboveMatchingRight, relativeTo: view1, padding: 10, width: 60)
206 | view8.alignAndFillWidth(align: .toTheLeftMatchingTop, relativeTo: view7, padding: 10, height: 70)
207 | view9.alignBetweenVertical(align: .underMatchingLeft, primaryView: view8, secondaryView: anchorView, padding: 10, width: 100)
208 | view10.alignBetweenHorizontal(align: .toTheRightMatchingTop, primaryView: view9, secondaryView: view7, padding: 10, height: view9.height)
209 | view14.anchorInCorner(.bottomLeft, xPad: 10, yPad: 10, width: 100, height: 100)
210 | view15.alignBetweenVertical(align: .underMatchingLeft, primaryView: view9, secondaryView: view14, padding: 10, width: 50)
211 | view16.alignBetweenHorizontal(align: .toTheRightMatchingBottom, primaryView: view14, secondaryView: view6, padding: 10, height: 40)
212 | view17.alignBetweenHorizontal(align: .toTheRightMatchingTop, primaryView: view15, secondaryView: anchorView, padding: 10, height: 100)
213 | view18.alignBetweenVertical(align: .underMatchingLeft, primaryView: anchorView, secondaryView: view16, padding: 10, width: anchorView.width)
214 | view19.alignBetweenHorizontal(align: .toTheRightMatchingTop, primaryView: view14, secondaryView: view18, padding: 10, height: 50)
215 | view20.alignBetweenVertical(align: .underCentered, primaryView: view17, secondaryView: view19, padding: 10, width: view17.width)
216 | }
217 |
218 | override func touchesMoved(_ touches: Set, with event: UIEvent?) {
219 | let touch : UITouch = touches.first!
220 | let point = touch.location(in: containerView)
221 |
222 | anchorView.frame = CGRect(x: point.x - (anchorView.width / 2.0), y: point.y - (anchorView.height / 2.0), width: anchorView.width, height: anchorView.height)
223 |
224 | layoutFrames()
225 | }
226 | }
227 |
228 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Michael Amaral
2 |
3 | MIT LICENSE
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Neon.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'Neon'
3 | s.version = '0.4.0'
4 | s.license = 'MIT'
5 | s.summary = 'A powerful Swift programmatic UI layout framework.'
6 | s.homepage = 'https://github.com/mamaral/neon'
7 | s.social_media_url = 'http://twitter.com/MikeAmaral'
8 | s.authors = { 'Mike Amaral' => 'mike.amaral36@gmail.com' }
9 | s.source = { :git => 'https://github.com/mamaral/neon.git', :tag => 'v0.4.0' }
10 | s.osx.deployment_target = '10.11'
11 | s.ios.deployment_target = '9.0'
12 | s.source_files = 'Source/*.swift'
13 | s.requires_arc = true
14 | end
15 |
--------------------------------------------------------------------------------
/Neon.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 47;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 033D78F11BBF900A0034752F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 033D78F01BBF900A0034752F /* Assets.xcassets */; };
11 | 033D78F81BBF90860034752F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 033D78F61BBF90860034752F /* LaunchScreen.storyboard */; };
12 | 036A08351BBF919E000B093A /* NeonUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036A08341BBF919E000B093A /* NeonUnitTests.swift */; };
13 | 036A08371BBF919E000B093A /* Neon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03EDD3831BBF89910006C4A9 /* Neon.framework */; };
14 | 03EDD38A1BBF89910006C4A9 /* Neon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03EDD3831BBF89910006C4A9 /* Neon.framework */; };
15 | 03EDD38B1BBF89910006C4A9 /* Neon.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 03EDD3831BBF89910006C4A9 /* Neon.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
16 | 03EDD3911BBF89E90006C4A9 /* Neon.h in Headers */ = {isa = PBXBuildFile; fileRef = 03EDD3901BBF89E90006C4A9 /* Neon.h */; settings = {ATTRIBUTES = (Public, ); }; };
17 | 03EDD3A11BBF8A820006C4A9 /* Neon.h in Headers */ = {isa = PBXBuildFile; fileRef = 03EDD3901BBF89E90006C4A9 /* Neon.h */; settings = {ATTRIBUTES = (Public, ); }; };
18 | 03EDD3B31BBF8BC90006C4A9 /* Neon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3AE1BBF8BC90006C4A9 /* Neon.swift */; };
19 | 03EDD3B41BBF8BCA0006C4A9 /* Neon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3AE1BBF8BC90006C4A9 /* Neon.swift */; };
20 | 03EDD3B51BBF8BCA0006C4A9 /* NeonAlignable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3AF1BBF8BC90006C4A9 /* NeonAlignable.swift */; };
21 | 03EDD3B61BBF8BCA0006C4A9 /* NeonAlignable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3AF1BBF8BC90006C4A9 /* NeonAlignable.swift */; };
22 | 03EDD3B71BBF8BCA0006C4A9 /* NeonAnchorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3B01BBF8BC90006C4A9 /* NeonAnchorable.swift */; };
23 | 03EDD3B81BBF8BCA0006C4A9 /* NeonAnchorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3B01BBF8BC90006C4A9 /* NeonAnchorable.swift */; };
24 | 03EDD3B91BBF8BCA0006C4A9 /* NeonFrameable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3B11BBF8BC90006C4A9 /* NeonFrameable.swift */; };
25 | 03EDD3BA1BBF8BCA0006C4A9 /* NeonFrameable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3B11BBF8BC90006C4A9 /* NeonFrameable.swift */; };
26 | 03EDD3BB1BBF8BCA0006C4A9 /* NeonGroupable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3B21BBF8BC90006C4A9 /* NeonGroupable.swift */; };
27 | 03EDD3BC1BBF8BCA0006C4A9 /* NeonGroupable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3B21BBF8BC90006C4A9 /* NeonGroupable.swift */; };
28 | 03EDD3C41BBF8CEC0006C4A9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3BD1BBF8CEC0006C4A9 /* AppDelegate.swift */; };
29 | 03EDD3C51BBF8CEC0006C4A9 /* FacebookProfileExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3BE1BBF8CEC0006C4A9 /* FacebookProfileExampleViewController.swift */; };
30 | 03EDD3C61BBF8CEC0006C4A9 /* IconButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3BF1BBF8CEC0006C4A9 /* IconButton.swift */; };
31 | 03EDD3C71BBF8CEC0006C4A9 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3C01BBF8CEC0006C4A9 /* ImageContainerView.swift */; };
32 | 03EDD3C81BBF8CEC0006C4A9 /* TestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3C21BBF8CEC0006C4A9 /* TestViewController.swift */; };
33 | 03EDD3C91BBF8CEC0006C4A9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EDD3C31BBF8CEC0006C4A9 /* ViewController.swift */; };
34 | 52E7D6F71D520983007288D6 /* NeonExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52E7D6F61D520983007288D6 /* NeonExtensions.swift */; };
35 | 52E7D6F81D520983007288D6 /* NeonExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52E7D6F61D520983007288D6 /* NeonExtensions.swift */; };
36 | /* End PBXBuildFile section */
37 |
38 | /* Begin PBXContainerItemProxy section */
39 | 036A08381BBF919E000B093A /* PBXContainerItemProxy */ = {
40 | isa = PBXContainerItemProxy;
41 | containerPortal = 27D2535E1BAF71AB00FA4FED /* Project object */;
42 | proxyType = 1;
43 | remoteGlobalIDString = 03EDD3821BBF89910006C4A9;
44 | remoteInfo = "Neon-iOS";
45 | };
46 | 03EDD3881BBF89910006C4A9 /* PBXContainerItemProxy */ = {
47 | isa = PBXContainerItemProxy;
48 | containerPortal = 27D2535E1BAF71AB00FA4FED /* Project object */;
49 | proxyType = 1;
50 | remoteGlobalIDString = 03EDD3821BBF89910006C4A9;
51 | remoteInfo = "Neon-iOS";
52 | };
53 | /* End PBXContainerItemProxy section */
54 |
55 | /* Begin PBXCopyFilesBuildPhase section */
56 | 03EDD38F1BBF89910006C4A9 /* Embed Frameworks */ = {
57 | isa = PBXCopyFilesBuildPhase;
58 | buildActionMask = 2147483647;
59 | dstPath = "";
60 | dstSubfolderSpec = 10;
61 | files = (
62 | 03EDD38B1BBF89910006C4A9 /* Neon.framework in Embed Frameworks */,
63 | );
64 | name = "Embed Frameworks";
65 | runOnlyForDeploymentPostprocessing = 0;
66 | };
67 | /* End PBXCopyFilesBuildPhase section */
68 |
69 | /* Begin PBXFileReference section */
70 | 033D78F01BBF900A0034752F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Demo/Assets.xcassets; sourceTree = SOURCE_ROOT; };
71 | 033D78F21BBF90140034752F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Demo/Info.plist; sourceTree = SOURCE_ROOT; };
72 | 033D78F71BBF90860034752F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Demo/Base.lproj/LaunchScreen.storyboard; sourceTree = SOURCE_ROOT; };
73 | 036A08321BBF919E000B093A /* NeonUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NeonUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
74 | 036A08341BBF919E000B093A /* NeonUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeonUnitTests.swift; sourceTree = ""; };
75 | 036A08361BBF919E000B093A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
76 | 03EDD3831BBF89910006C4A9 /* Neon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Neon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
77 | 03EDD3901BBF89E90006C4A9 /* Neon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Neon.h; path = Source/Neon.h; sourceTree = SOURCE_ROOT; };
78 | 03EDD3971BBF8A4F0006C4A9 /* Neon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Neon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
79 | 03EDD3A01BBF8A7E0006C4A9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Source/Info.plist; sourceTree = SOURCE_ROOT; };
80 | 03EDD3AE1BBF8BC90006C4A9 /* Neon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Neon.swift; path = Source/Neon.swift; sourceTree = SOURCE_ROOT; };
81 | 03EDD3AF1BBF8BC90006C4A9 /* NeonAlignable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NeonAlignable.swift; path = Source/NeonAlignable.swift; sourceTree = SOURCE_ROOT; };
82 | 03EDD3B01BBF8BC90006C4A9 /* NeonAnchorable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NeonAnchorable.swift; path = Source/NeonAnchorable.swift; sourceTree = SOURCE_ROOT; };
83 | 03EDD3B11BBF8BC90006C4A9 /* NeonFrameable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NeonFrameable.swift; path = Source/NeonFrameable.swift; sourceTree = SOURCE_ROOT; };
84 | 03EDD3B21BBF8BC90006C4A9 /* NeonGroupable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NeonGroupable.swift; path = Source/NeonGroupable.swift; sourceTree = SOURCE_ROOT; };
85 | 03EDD3BD1BBF8CEC0006C4A9 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Demo/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
86 | 03EDD3BE1BBF8CEC0006C4A9 /* FacebookProfileExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FacebookProfileExampleViewController.swift; path = Demo/FacebookProfileExampleViewController.swift; sourceTree = SOURCE_ROOT; };
87 | 03EDD3BF1BBF8CEC0006C4A9 /* IconButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IconButton.swift; path = Demo/IconButton.swift; sourceTree = SOURCE_ROOT; };
88 | 03EDD3C01BBF8CEC0006C4A9 /* ImageContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageContainerView.swift; path = Demo/ImageContainerView.swift; sourceTree = SOURCE_ROOT; };
89 | 03EDD3C21BBF8CEC0006C4A9 /* TestViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TestViewController.swift; path = Demo/TestViewController.swift; sourceTree = SOURCE_ROOT; };
90 | 03EDD3C31BBF8CEC0006C4A9 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = Demo/ViewController.swift; sourceTree = SOURCE_ROOT; };
91 | 27D253661BAF71AB00FA4FED /* Neon_Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Neon_Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
92 | 52E7D6F61D520983007288D6 /* NeonExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NeonExtensions.swift; path = Source/NeonExtensions.swift; sourceTree = SOURCE_ROOT; };
93 | /* End PBXFileReference section */
94 |
95 | /* Begin PBXFrameworksBuildPhase section */
96 | 036A082F1BBF919E000B093A /* Frameworks */ = {
97 | isa = PBXFrameworksBuildPhase;
98 | buildActionMask = 2147483647;
99 | files = (
100 | 036A08371BBF919E000B093A /* Neon.framework in Frameworks */,
101 | );
102 | runOnlyForDeploymentPostprocessing = 0;
103 | };
104 | 03EDD37F1BBF89910006C4A9 /* Frameworks */ = {
105 | isa = PBXFrameworksBuildPhase;
106 | buildActionMask = 2147483647;
107 | files = (
108 | );
109 | runOnlyForDeploymentPostprocessing = 0;
110 | };
111 | 03EDD3931BBF8A4F0006C4A9 /* Frameworks */ = {
112 | isa = PBXFrameworksBuildPhase;
113 | buildActionMask = 2147483647;
114 | files = (
115 | );
116 | runOnlyForDeploymentPostprocessing = 0;
117 | };
118 | 27D253631BAF71AB00FA4FED /* Frameworks */ = {
119 | isa = PBXFrameworksBuildPhase;
120 | buildActionMask = 2147483647;
121 | files = (
122 | 03EDD38A1BBF89910006C4A9 /* Neon.framework in Frameworks */,
123 | );
124 | runOnlyForDeploymentPostprocessing = 0;
125 | };
126 | /* End PBXFrameworksBuildPhase section */
127 |
128 | /* Begin PBXGroup section */
129 | 036A08331BBF919E000B093A /* NeonUnitTests */ = {
130 | isa = PBXGroup;
131 | children = (
132 | 036A08341BBF919E000B093A /* NeonUnitTests.swift */,
133 | 036A08361BBF919E000B093A /* Info.plist */,
134 | );
135 | path = NeonUnitTests;
136 | sourceTree = "";
137 | };
138 | 03EDD37D1BBF89350006C4A9 /* Tests */ = {
139 | isa = PBXGroup;
140 | children = (
141 | );
142 | name = Tests;
143 | sourceTree = "";
144 | };
145 | 03EDD39F1BBF8A6A0006C4A9 /* Supporting Files */ = {
146 | isa = PBXGroup;
147 | children = (
148 | 03EDD3901BBF89E90006C4A9 /* Neon.h */,
149 | 03EDD3A01BBF8A7E0006C4A9 /* Info.plist */,
150 | );
151 | name = "Supporting Files";
152 | sourceTree = "";
153 | };
154 | 27D2535D1BAF71AB00FA4FED = {
155 | isa = PBXGroup;
156 | children = (
157 | 27D253971BAF71C700FA4FED /* Source */,
158 | 27D253681BAF71AB00FA4FED /* Demo */,
159 | 036A08331BBF919E000B093A /* NeonUnitTests */,
160 | 27D253671BAF71AB00FA4FED /* Products */,
161 | 03EDD37D1BBF89350006C4A9 /* Tests */,
162 | );
163 | sourceTree = "";
164 | };
165 | 27D253671BAF71AB00FA4FED /* Products */ = {
166 | isa = PBXGroup;
167 | children = (
168 | 27D253661BAF71AB00FA4FED /* Neon_Demo.app */,
169 | 03EDD3831BBF89910006C4A9 /* Neon.framework */,
170 | 03EDD3971BBF8A4F0006C4A9 /* Neon.framework */,
171 | 036A08321BBF919E000B093A /* NeonUnitTests.xctest */,
172 | );
173 | name = Products;
174 | sourceTree = "";
175 | };
176 | 27D253681BAF71AB00FA4FED /* Demo */ = {
177 | isa = PBXGroup;
178 | children = (
179 | 03EDD3BD1BBF8CEC0006C4A9 /* AppDelegate.swift */,
180 | 03EDD3BF1BBF8CEC0006C4A9 /* IconButton.swift */,
181 | 03EDD3C31BBF8CEC0006C4A9 /* ViewController.swift */,
182 | 03EDD3C21BBF8CEC0006C4A9 /* TestViewController.swift */,
183 | 03EDD3C01BBF8CEC0006C4A9 /* ImageContainerView.swift */,
184 | 03EDD3BE1BBF8CEC0006C4A9 /* FacebookProfileExampleViewController.swift */,
185 | 27D253A01BAF735300FA4FED /* Supporting Files */,
186 | );
187 | name = Demo;
188 | path = Neon;
189 | sourceTree = "";
190 | };
191 | 27D253971BAF71C700FA4FED /* Source */ = {
192 | isa = PBXGroup;
193 | children = (
194 | 03EDD3AE1BBF8BC90006C4A9 /* Neon.swift */,
195 | 52E7D6F61D520983007288D6 /* NeonExtensions.swift */,
196 | 03EDD3B11BBF8BC90006C4A9 /* NeonFrameable.swift */,
197 | 03EDD3B01BBF8BC90006C4A9 /* NeonAnchorable.swift */,
198 | 03EDD3AF1BBF8BC90006C4A9 /* NeonAlignable.swift */,
199 | 03EDD3B21BBF8BC90006C4A9 /* NeonGroupable.swift */,
200 | 03EDD39F1BBF8A6A0006C4A9 /* Supporting Files */,
201 | );
202 | name = Source;
203 | path = ../../Source;
204 | sourceTree = "";
205 | };
206 | 27D253A01BAF735300FA4FED /* Supporting Files */ = {
207 | isa = PBXGroup;
208 | children = (
209 | 033D78F61BBF90860034752F /* LaunchScreen.storyboard */,
210 | 033D78F01BBF900A0034752F /* Assets.xcassets */,
211 | 033D78F21BBF90140034752F /* Info.plist */,
212 | );
213 | name = "Supporting Files";
214 | sourceTree = "";
215 | };
216 | /* End PBXGroup section */
217 |
218 | /* Begin PBXHeadersBuildPhase section */
219 | 03EDD3801BBF89910006C4A9 /* Headers */ = {
220 | isa = PBXHeadersBuildPhase;
221 | buildActionMask = 2147483647;
222 | files = (
223 | 03EDD3911BBF89E90006C4A9 /* Neon.h in Headers */,
224 | );
225 | runOnlyForDeploymentPostprocessing = 0;
226 | };
227 | 03EDD3941BBF8A4F0006C4A9 /* Headers */ = {
228 | isa = PBXHeadersBuildPhase;
229 | buildActionMask = 2147483647;
230 | files = (
231 | 03EDD3A11BBF8A820006C4A9 /* Neon.h in Headers */,
232 | );
233 | runOnlyForDeploymentPostprocessing = 0;
234 | };
235 | /* End PBXHeadersBuildPhase section */
236 |
237 | /* Begin PBXNativeTarget section */
238 | 036A08311BBF919E000B093A /* NeonUnitTests */ = {
239 | isa = PBXNativeTarget;
240 | buildConfigurationList = 036A083A1BBF919E000B093A /* Build configuration list for PBXNativeTarget "NeonUnitTests" */;
241 | buildPhases = (
242 | 036A082E1BBF919E000B093A /* Sources */,
243 | 036A082F1BBF919E000B093A /* Frameworks */,
244 | 036A08301BBF919E000B093A /* Resources */,
245 | );
246 | buildRules = (
247 | );
248 | dependencies = (
249 | 036A08391BBF919E000B093A /* PBXTargetDependency */,
250 | );
251 | name = NeonUnitTests;
252 | productName = NeonUnitTests;
253 | productReference = 036A08321BBF919E000B093A /* NeonUnitTests.xctest */;
254 | productType = "com.apple.product-type.bundle.unit-test";
255 | };
256 | 03EDD3821BBF89910006C4A9 /* Neon iOS */ = {
257 | isa = PBXNativeTarget;
258 | buildConfigurationList = 03EDD38C1BBF89910006C4A9 /* Build configuration list for PBXNativeTarget "Neon iOS" */;
259 | buildPhases = (
260 | 03EDD37E1BBF89910006C4A9 /* Sources */,
261 | 03EDD37F1BBF89910006C4A9 /* Frameworks */,
262 | 03EDD3801BBF89910006C4A9 /* Headers */,
263 | 03EDD3811BBF89910006C4A9 /* Resources */,
264 | );
265 | buildRules = (
266 | );
267 | dependencies = (
268 | );
269 | name = "Neon iOS";
270 | productName = "Neon-iOS";
271 | productReference = 03EDD3831BBF89910006C4A9 /* Neon.framework */;
272 | productType = "com.apple.product-type.framework";
273 | };
274 | 03EDD3961BBF8A4F0006C4A9 /* Neon OSX */ = {
275 | isa = PBXNativeTarget;
276 | buildConfigurationList = 03EDD39C1BBF8A4F0006C4A9 /* Build configuration list for PBXNativeTarget "Neon OSX" */;
277 | buildPhases = (
278 | 03EDD3921BBF8A4F0006C4A9 /* Sources */,
279 | 03EDD3931BBF8A4F0006C4A9 /* Frameworks */,
280 | 03EDD3941BBF8A4F0006C4A9 /* Headers */,
281 | 03EDD3951BBF8A4F0006C4A9 /* Resources */,
282 | );
283 | buildRules = (
284 | );
285 | dependencies = (
286 | );
287 | name = "Neon OSX";
288 | productName = "Neon-OSX";
289 | productReference = 03EDD3971BBF8A4F0006C4A9 /* Neon.framework */;
290 | productType = "com.apple.product-type.framework";
291 | };
292 | 27D253651BAF71AB00FA4FED /* Neon Demo */ = {
293 | isa = PBXNativeTarget;
294 | buildConfigurationList = 27D2538E1BAF71AB00FA4FED /* Build configuration list for PBXNativeTarget "Neon Demo" */;
295 | buildPhases = (
296 | 27D253621BAF71AB00FA4FED /* Sources */,
297 | 27D253631BAF71AB00FA4FED /* Frameworks */,
298 | 27D253641BAF71AB00FA4FED /* Resources */,
299 | 03EDD38F1BBF89910006C4A9 /* Embed Frameworks */,
300 | );
301 | buildRules = (
302 | );
303 | dependencies = (
304 | 03EDD3891BBF89910006C4A9 /* PBXTargetDependency */,
305 | );
306 | name = "Neon Demo";
307 | productName = Neon;
308 | productReference = 27D253661BAF71AB00FA4FED /* Neon_Demo.app */;
309 | productType = "com.apple.product-type.application";
310 | };
311 | /* End PBXNativeTarget section */
312 |
313 | /* Begin PBXProject section */
314 | 27D2535E1BAF71AB00FA4FED /* Project object */ = {
315 | isa = PBXProject;
316 | attributes = {
317 | LastSwiftUpdateCheck = 0710;
318 | LastUpgradeCheck = 0900;
319 | ORGANIZATIONNAME = "Mike Amaral";
320 | TargetAttributes = {
321 | 036A08311BBF919E000B093A = {
322 | CreatedOnToolsVersion = 7.1;
323 | LastSwiftMigration = 0800;
324 | };
325 | 03EDD3821BBF89910006C4A9 = {
326 | CreatedOnToolsVersion = 7.1;
327 | LastSwiftMigration = 0900;
328 | };
329 | 03EDD3961BBF8A4F0006C4A9 = {
330 | CreatedOnToolsVersion = 7.1;
331 | LastSwiftMigration = 0800;
332 | };
333 | 27D253651BAF71AB00FA4FED = {
334 | CreatedOnToolsVersion = 7.0;
335 | DevelopmentTeam = 9W3UPW3F39;
336 | LastSwiftMigration = 0900;
337 | ProvisioningStyle = Automatic;
338 | };
339 | };
340 | };
341 | buildConfigurationList = 27D253611BAF71AB00FA4FED /* Build configuration list for PBXProject "Neon" */;
342 | compatibilityVersion = "Xcode 6.3";
343 | developmentRegion = English;
344 | hasScannedForEncodings = 0;
345 | knownRegions = (
346 | en,
347 | Base,
348 | );
349 | mainGroup = 27D2535D1BAF71AB00FA4FED;
350 | productRefGroup = 27D253671BAF71AB00FA4FED /* Products */;
351 | projectDirPath = "";
352 | projectRoot = "";
353 | targets = (
354 | 03EDD3821BBF89910006C4A9 /* Neon iOS */,
355 | 03EDD3961BBF8A4F0006C4A9 /* Neon OSX */,
356 | 27D253651BAF71AB00FA4FED /* Neon Demo */,
357 | 036A08311BBF919E000B093A /* NeonUnitTests */,
358 | );
359 | };
360 | /* End PBXProject section */
361 |
362 | /* Begin PBXResourcesBuildPhase section */
363 | 036A08301BBF919E000B093A /* Resources */ = {
364 | isa = PBXResourcesBuildPhase;
365 | buildActionMask = 2147483647;
366 | files = (
367 | );
368 | runOnlyForDeploymentPostprocessing = 0;
369 | };
370 | 03EDD3811BBF89910006C4A9 /* Resources */ = {
371 | isa = PBXResourcesBuildPhase;
372 | buildActionMask = 2147483647;
373 | files = (
374 | );
375 | runOnlyForDeploymentPostprocessing = 0;
376 | };
377 | 03EDD3951BBF8A4F0006C4A9 /* Resources */ = {
378 | isa = PBXResourcesBuildPhase;
379 | buildActionMask = 2147483647;
380 | files = (
381 | );
382 | runOnlyForDeploymentPostprocessing = 0;
383 | };
384 | 27D253641BAF71AB00FA4FED /* Resources */ = {
385 | isa = PBXResourcesBuildPhase;
386 | buildActionMask = 2147483647;
387 | files = (
388 | 033D78F11BBF900A0034752F /* Assets.xcassets in Resources */,
389 | 033D78F81BBF90860034752F /* LaunchScreen.storyboard in Resources */,
390 | );
391 | runOnlyForDeploymentPostprocessing = 0;
392 | };
393 | /* End PBXResourcesBuildPhase section */
394 |
395 | /* Begin PBXSourcesBuildPhase section */
396 | 036A082E1BBF919E000B093A /* Sources */ = {
397 | isa = PBXSourcesBuildPhase;
398 | buildActionMask = 2147483647;
399 | files = (
400 | 036A08351BBF919E000B093A /* NeonUnitTests.swift in Sources */,
401 | );
402 | runOnlyForDeploymentPostprocessing = 0;
403 | };
404 | 03EDD37E1BBF89910006C4A9 /* Sources */ = {
405 | isa = PBXSourcesBuildPhase;
406 | buildActionMask = 2147483647;
407 | files = (
408 | 03EDD3B31BBF8BC90006C4A9 /* Neon.swift in Sources */,
409 | 52E7D6F71D520983007288D6 /* NeonExtensions.swift in Sources */,
410 | 03EDD3BB1BBF8BCA0006C4A9 /* NeonGroupable.swift in Sources */,
411 | 03EDD3B91BBF8BCA0006C4A9 /* NeonFrameable.swift in Sources */,
412 | 03EDD3B51BBF8BCA0006C4A9 /* NeonAlignable.swift in Sources */,
413 | 03EDD3B71BBF8BCA0006C4A9 /* NeonAnchorable.swift in Sources */,
414 | );
415 | runOnlyForDeploymentPostprocessing = 0;
416 | };
417 | 03EDD3921BBF8A4F0006C4A9 /* Sources */ = {
418 | isa = PBXSourcesBuildPhase;
419 | buildActionMask = 2147483647;
420 | files = (
421 | 03EDD3B41BBF8BCA0006C4A9 /* Neon.swift in Sources */,
422 | 52E7D6F81D520983007288D6 /* NeonExtensions.swift in Sources */,
423 | 03EDD3BC1BBF8BCA0006C4A9 /* NeonGroupable.swift in Sources */,
424 | 03EDD3BA1BBF8BCA0006C4A9 /* NeonFrameable.swift in Sources */,
425 | 03EDD3B61BBF8BCA0006C4A9 /* NeonAlignable.swift in Sources */,
426 | 03EDD3B81BBF8BCA0006C4A9 /* NeonAnchorable.swift in Sources */,
427 | );
428 | runOnlyForDeploymentPostprocessing = 0;
429 | };
430 | 27D253621BAF71AB00FA4FED /* Sources */ = {
431 | isa = PBXSourcesBuildPhase;
432 | buildActionMask = 2147483647;
433 | files = (
434 | 03EDD3C91BBF8CEC0006C4A9 /* ViewController.swift in Sources */,
435 | 03EDD3C81BBF8CEC0006C4A9 /* TestViewController.swift in Sources */,
436 | 03EDD3C71BBF8CEC0006C4A9 /* ImageContainerView.swift in Sources */,
437 | 03EDD3C61BBF8CEC0006C4A9 /* IconButton.swift in Sources */,
438 | 03EDD3C41BBF8CEC0006C4A9 /* AppDelegate.swift in Sources */,
439 | 03EDD3C51BBF8CEC0006C4A9 /* FacebookProfileExampleViewController.swift in Sources */,
440 | );
441 | runOnlyForDeploymentPostprocessing = 0;
442 | };
443 | /* End PBXSourcesBuildPhase section */
444 |
445 | /* Begin PBXTargetDependency section */
446 | 036A08391BBF919E000B093A /* PBXTargetDependency */ = {
447 | isa = PBXTargetDependency;
448 | target = 03EDD3821BBF89910006C4A9 /* Neon iOS */;
449 | targetProxy = 036A08381BBF919E000B093A /* PBXContainerItemProxy */;
450 | };
451 | 03EDD3891BBF89910006C4A9 /* PBXTargetDependency */ = {
452 | isa = PBXTargetDependency;
453 | target = 03EDD3821BBF89910006C4A9 /* Neon iOS */;
454 | targetProxy = 03EDD3881BBF89910006C4A9 /* PBXContainerItemProxy */;
455 | };
456 | /* End PBXTargetDependency section */
457 |
458 | /* Begin PBXVariantGroup section */
459 | 033D78F61BBF90860034752F /* LaunchScreen.storyboard */ = {
460 | isa = PBXVariantGroup;
461 | children = (
462 | 033D78F71BBF90860034752F /* Base */,
463 | );
464 | name = LaunchScreen.storyboard;
465 | sourceTree = "";
466 | };
467 | /* End PBXVariantGroup section */
468 |
469 | /* Begin XCBuildConfiguration section */
470 | 036A083B1BBF919E000B093A /* Debug */ = {
471 | isa = XCBuildConfiguration;
472 | buildSettings = {
473 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
474 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
475 | INFOPLIST_FILE = NeonUnitTests/Info.plist;
476 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
477 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
478 | PRODUCT_BUNDLE_IDENTIFIER = com.extremelylimited.NeonUnitTests;
479 | PRODUCT_NAME = "$(TARGET_NAME)";
480 | SWIFT_VERSION = 3.0;
481 | };
482 | name = Debug;
483 | };
484 | 036A083C1BBF919E000B093A /* Release */ = {
485 | isa = XCBuildConfiguration;
486 | buildSettings = {
487 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
488 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
489 | INFOPLIST_FILE = NeonUnitTests/Info.plist;
490 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
491 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
492 | PRODUCT_BUNDLE_IDENTIFIER = com.extremelylimited.NeonUnitTests;
493 | PRODUCT_NAME = "$(TARGET_NAME)";
494 | SWIFT_VERSION = 3.0;
495 | };
496 | name = Release;
497 | };
498 | 03EDD38D1BBF89910006C4A9 /* Debug */ = {
499 | isa = XCBuildConfiguration;
500 | buildSettings = {
501 | CLANG_ENABLE_MODULES = YES;
502 | CODE_SIGN_IDENTITY = "";
503 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
504 | CURRENT_PROJECT_VERSION = 1;
505 | DEFINES_MODULE = YES;
506 | DYLIB_COMPATIBILITY_VERSION = 1;
507 | DYLIB_CURRENT_VERSION = 1;
508 | DYLIB_INSTALL_NAME_BASE = "@rpath";
509 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
510 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
511 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
512 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
513 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
514 | OTHER_LDFLAGS = (
515 | "-D",
516 | DEBUG,
517 | );
518 | PRODUCT_BUNDLE_IDENTIFIER = com.MikeAmaral.NeoniOS;
519 | PRODUCT_NAME = Neon;
520 | SKIP_INSTALL = YES;
521 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
522 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
523 | SWIFT_VERSION = 4.0;
524 | VERSIONING_SYSTEM = "apple-generic";
525 | VERSION_INFO_PREFIX = "";
526 | };
527 | name = Debug;
528 | };
529 | 03EDD38E1BBF89910006C4A9 /* Release */ = {
530 | isa = XCBuildConfiguration;
531 | buildSettings = {
532 | CLANG_ENABLE_MODULES = YES;
533 | CODE_SIGN_IDENTITY = "";
534 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
535 | CURRENT_PROJECT_VERSION = 1;
536 | DEFINES_MODULE = YES;
537 | DYLIB_COMPATIBILITY_VERSION = 1;
538 | DYLIB_CURRENT_VERSION = 1;
539 | DYLIB_INSTALL_NAME_BASE = "@rpath";
540 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
541 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
542 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
543 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
544 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
545 | OTHER_LDFLAGS = (
546 | "-D",
547 | DEBUG,
548 | );
549 | PRODUCT_BUNDLE_IDENTIFIER = com.MikeAmaral.NeoniOS;
550 | PRODUCT_NAME = Neon;
551 | SKIP_INSTALL = YES;
552 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
553 | SWIFT_VERSION = 4.0;
554 | VERSIONING_SYSTEM = "apple-generic";
555 | VERSION_INFO_PREFIX = "";
556 | };
557 | name = Release;
558 | };
559 | 03EDD39D1BBF8A4F0006C4A9 /* Debug */ = {
560 | isa = XCBuildConfiguration;
561 | buildSettings = {
562 | CLANG_ENABLE_MODULES = YES;
563 | CODE_SIGN_IDENTITY = "";
564 | COMBINE_HIDPI_IMAGES = YES;
565 | CURRENT_PROJECT_VERSION = 1;
566 | DEFINES_MODULE = YES;
567 | DYLIB_COMPATIBILITY_VERSION = 1;
568 | DYLIB_CURRENT_VERSION = 1;
569 | DYLIB_INSTALL_NAME_BASE = "@rpath";
570 | FRAMEWORK_VERSION = A;
571 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
572 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
573 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
574 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
575 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
576 | MACOSX_DEPLOYMENT_TARGET = 10.9;
577 | PRODUCT_BUNDLE_IDENTIFIER = com.MikeAmaral.NeonOSX;
578 | PRODUCT_NAME = Neon;
579 | SDKROOT = macosx;
580 | SKIP_INSTALL = YES;
581 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
582 | SWIFT_VERSION = 4.0;
583 | VERSIONING_SYSTEM = "apple-generic";
584 | VERSION_INFO_PREFIX = "";
585 | };
586 | name = Debug;
587 | };
588 | 03EDD39E1BBF8A4F0006C4A9 /* Release */ = {
589 | isa = XCBuildConfiguration;
590 | buildSettings = {
591 | CLANG_ENABLE_MODULES = YES;
592 | CODE_SIGN_IDENTITY = "";
593 | COMBINE_HIDPI_IMAGES = YES;
594 | CURRENT_PROJECT_VERSION = 1;
595 | DEFINES_MODULE = YES;
596 | DYLIB_COMPATIBILITY_VERSION = 1;
597 | DYLIB_CURRENT_VERSION = 1;
598 | DYLIB_INSTALL_NAME_BASE = "@rpath";
599 | FRAMEWORK_VERSION = A;
600 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
601 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
602 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
603 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
604 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
605 | MACOSX_DEPLOYMENT_TARGET = 10.9;
606 | PRODUCT_BUNDLE_IDENTIFIER = com.MikeAmaral.NeonOSX;
607 | PRODUCT_NAME = Neon;
608 | SDKROOT = macosx;
609 | SKIP_INSTALL = YES;
610 | SWIFT_VERSION = 4.0;
611 | VERSIONING_SYSTEM = "apple-generic";
612 | VERSION_INFO_PREFIX = "";
613 | };
614 | name = Release;
615 | };
616 | 27D2538C1BAF71AB00FA4FED /* Debug */ = {
617 | isa = XCBuildConfiguration;
618 | buildSettings = {
619 | ALWAYS_SEARCH_USER_PATHS = NO;
620 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
621 | CLANG_CXX_LIBRARY = "libc++";
622 | CLANG_ENABLE_MODULES = YES;
623 | CLANG_ENABLE_OBJC_ARC = YES;
624 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
625 | CLANG_WARN_BOOL_CONVERSION = YES;
626 | CLANG_WARN_COMMA = YES;
627 | CLANG_WARN_CONSTANT_CONVERSION = YES;
628 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
629 | CLANG_WARN_EMPTY_BODY = YES;
630 | CLANG_WARN_ENUM_CONVERSION = YES;
631 | CLANG_WARN_INFINITE_RECURSION = YES;
632 | CLANG_WARN_INT_CONVERSION = YES;
633 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
634 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
635 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
636 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
637 | CLANG_WARN_STRICT_PROTOTYPES = YES;
638 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
639 | CLANG_WARN_UNREACHABLE_CODE = YES;
640 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
641 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
642 | COPY_PHASE_STRIP = NO;
643 | DEBUG_INFORMATION_FORMAT = dwarf;
644 | ENABLE_STRICT_OBJC_MSGSEND = YES;
645 | ENABLE_TESTABILITY = YES;
646 | GCC_C_LANGUAGE_STANDARD = gnu99;
647 | GCC_DYNAMIC_NO_PIC = NO;
648 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
649 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
650 | GCC_NO_COMMON_BLOCKS = YES;
651 | GCC_OPTIMIZATION_LEVEL = 0;
652 | GCC_PREPROCESSOR_DEFINITIONS = (
653 | "DEBUG=1",
654 | "$(inherited)",
655 | );
656 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
657 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
658 | GCC_WARN_UNDECLARED_SELECTOR = YES;
659 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
660 | GCC_WARN_UNUSED_FUNCTION = YES;
661 | GCC_WARN_UNUSED_VARIABLE = YES;
662 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
663 | MACOSX_DEPLOYMENT_TARGET = 10.9;
664 | MTL_ENABLE_DEBUG_INFO = YES;
665 | ONLY_ACTIVE_ARCH = YES;
666 | SDKROOT = iphoneos;
667 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
668 | TARGETED_DEVICE_FAMILY = "1,2";
669 | };
670 | name = Debug;
671 | };
672 | 27D2538D1BAF71AB00FA4FED /* Release */ = {
673 | isa = XCBuildConfiguration;
674 | buildSettings = {
675 | ALWAYS_SEARCH_USER_PATHS = NO;
676 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
677 | CLANG_CXX_LIBRARY = "libc++";
678 | CLANG_ENABLE_MODULES = YES;
679 | CLANG_ENABLE_OBJC_ARC = YES;
680 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
681 | CLANG_WARN_BOOL_CONVERSION = YES;
682 | CLANG_WARN_COMMA = YES;
683 | CLANG_WARN_CONSTANT_CONVERSION = YES;
684 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
685 | CLANG_WARN_EMPTY_BODY = YES;
686 | CLANG_WARN_ENUM_CONVERSION = YES;
687 | CLANG_WARN_INFINITE_RECURSION = YES;
688 | CLANG_WARN_INT_CONVERSION = YES;
689 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
690 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
691 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
692 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
693 | CLANG_WARN_STRICT_PROTOTYPES = YES;
694 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
695 | CLANG_WARN_UNREACHABLE_CODE = YES;
696 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
697 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
698 | COPY_PHASE_STRIP = NO;
699 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
700 | ENABLE_NS_ASSERTIONS = NO;
701 | ENABLE_STRICT_OBJC_MSGSEND = YES;
702 | GCC_C_LANGUAGE_STANDARD = gnu99;
703 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
704 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
705 | GCC_NO_COMMON_BLOCKS = YES;
706 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
707 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
708 | GCC_WARN_UNDECLARED_SELECTOR = YES;
709 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
710 | GCC_WARN_UNUSED_FUNCTION = YES;
711 | GCC_WARN_UNUSED_VARIABLE = YES;
712 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
713 | MACOSX_DEPLOYMENT_TARGET = 10.9;
714 | MTL_ENABLE_DEBUG_INFO = NO;
715 | SDKROOT = iphoneos;
716 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
717 | TARGETED_DEVICE_FAMILY = "1,2";
718 | VALIDATE_PRODUCT = YES;
719 | };
720 | name = Release;
721 | };
722 | 27D2538F1BAF71AB00FA4FED /* Debug */ = {
723 | isa = XCBuildConfiguration;
724 | buildSettings = {
725 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
726 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
727 | CLANG_ENABLE_MODULES = YES;
728 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
729 | DEVELOPMENT_TEAM = 9W3UPW3F39;
730 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
731 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
732 | INFOPLIST_FILE = "$(SRCROOT)/Demo/Info.plist";
733 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
734 | PRODUCT_BUNDLE_IDENTIFIER = com.MikeAmaral.NeonDemo;
735 | PRODUCT_MODULE_NAME = Neon_Demo;
736 | PRODUCT_NAME = Neon_Demo;
737 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
738 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
739 | SWIFT_VERSION = 4.0;
740 | };
741 | name = Debug;
742 | };
743 | 27D253901BAF71AB00FA4FED /* Release */ = {
744 | isa = XCBuildConfiguration;
745 | buildSettings = {
746 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
747 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
748 | CLANG_ENABLE_MODULES = YES;
749 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
750 | DEVELOPMENT_TEAM = 9W3UPW3F39;
751 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
752 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
753 | INFOPLIST_FILE = "$(SRCROOT)/Demo/Info.plist";
754 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
755 | PRODUCT_BUNDLE_IDENTIFIER = com.MikeAmaral.NeonDemo;
756 | PRODUCT_MODULE_NAME = Neon_Demo;
757 | PRODUCT_NAME = Neon_Demo;
758 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
759 | SWIFT_VERSION = 4.0;
760 | };
761 | name = Release;
762 | };
763 | /* End XCBuildConfiguration section */
764 |
765 | /* Begin XCConfigurationList section */
766 | 036A083A1BBF919E000B093A /* Build configuration list for PBXNativeTarget "NeonUnitTests" */ = {
767 | isa = XCConfigurationList;
768 | buildConfigurations = (
769 | 036A083B1BBF919E000B093A /* Debug */,
770 | 036A083C1BBF919E000B093A /* Release */,
771 | );
772 | defaultConfigurationIsVisible = 0;
773 | defaultConfigurationName = Release;
774 | };
775 | 03EDD38C1BBF89910006C4A9 /* Build configuration list for PBXNativeTarget "Neon iOS" */ = {
776 | isa = XCConfigurationList;
777 | buildConfigurations = (
778 | 03EDD38D1BBF89910006C4A9 /* Debug */,
779 | 03EDD38E1BBF89910006C4A9 /* Release */,
780 | );
781 | defaultConfigurationIsVisible = 0;
782 | defaultConfigurationName = Release;
783 | };
784 | 03EDD39C1BBF8A4F0006C4A9 /* Build configuration list for PBXNativeTarget "Neon OSX" */ = {
785 | isa = XCConfigurationList;
786 | buildConfigurations = (
787 | 03EDD39D1BBF8A4F0006C4A9 /* Debug */,
788 | 03EDD39E1BBF8A4F0006C4A9 /* Release */,
789 | );
790 | defaultConfigurationIsVisible = 0;
791 | defaultConfigurationName = Release;
792 | };
793 | 27D253611BAF71AB00FA4FED /* Build configuration list for PBXProject "Neon" */ = {
794 | isa = XCConfigurationList;
795 | buildConfigurations = (
796 | 27D2538C1BAF71AB00FA4FED /* Debug */,
797 | 27D2538D1BAF71AB00FA4FED /* Release */,
798 | );
799 | defaultConfigurationIsVisible = 0;
800 | defaultConfigurationName = Release;
801 | };
802 | 27D2538E1BAF71AB00FA4FED /* Build configuration list for PBXNativeTarget "Neon Demo" */ = {
803 | isa = XCConfigurationList;
804 | buildConfigurations = (
805 | 27D2538F1BAF71AB00FA4FED /* Debug */,
806 | 27D253901BAF71AB00FA4FED /* Release */,
807 | );
808 | defaultConfigurationIsVisible = 0;
809 | defaultConfigurationName = Release;
810 | };
811 | /* End XCConfigurationList section */
812 | };
813 | rootObject = 27D2535E1BAF71AB00FA4FED /* Project object */;
814 | }
815 |
--------------------------------------------------------------------------------
/Neon.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Neon.xcodeproj/xcshareddata/xcschemes/Neon Demo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/Neon.xcodeproj/xcshareddata/xcschemes/Neon OSX.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
72 |
73 |
74 |
75 |
77 |
78 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Neon.xcodeproj/xcshareddata/xcschemes/Neon iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
72 |
73 |
74 |
75 |
77 |
78 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Neon.xcodeproj/xcshareddata/xcschemes/NeonUnitTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
76 |
82 |
83 |
84 |
85 |
87 |
88 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/NeonUnitTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ### Build dynamic and beautiful user interfaces like a boss, with Swift.
4 |
5 | [](http://doge.mit-license.org) [](https://travis-ci.org/mamaral/Neon/) [](https://img.shields.io/cocoapods/v/Neon.svg) [](https://coveralls.io/r/mamaral/Neon?branch=master) [](https://github.com/Carthage/Carthage)
6 | [](https://cocoapods.org/pods/Neon)
7 |
8 | Neon is built around how user interfaces are naturally and intuitively designed. No more springs and struts. No more whacky visual format language. No more auto layout constraints. We're not robots, so why should we build our UIs like we are?
9 |
10 | > ***Neon has been updated to Swift 3.0, but is still currently in beta!***
11 |
12 | ## Install via CocoaPods
13 |
14 | You can use [Cocoapods](http://cocoapods.org/) to install `Neon` by adding it to your `Podfile`:
15 |
16 | ```ruby
17 | platform :ios, '9.0'
18 | use_frameworks!
19 | pod 'Neon'
20 | ```
21 |
22 | ##Manual Installation
23 |
24 | 1. Download and drop `/Source` in your project.
25 | 2. Congratulations!
26 |
27 |
28 | To get the full benefits import `Neon` wherever you have a UIView operation:
29 |
30 | ``` swift
31 | import UIKit
32 | import Neon
33 | ```
34 |
35 | ## Example
36 |
37 | 
38 |
39 | Rather than design some arbitrary layout for a demonstration, I figured a good test for the practicality of Neon would be to replicate an existing screen from a major app, one that everyone could recognize. The above screenshot on the left is my profile in the Facebook app, and the screenshot on the right is from the Neon demo project.
40 |
41 | Facebook's profile screen was surely built using some form of `UITableView` or `UICollectionView`, but for the sake of simple demonstration I built the top-most major components of the profile in a normal `UIViewController`. After all the customization of the subviews to make them as close to Facebook's design as possible *(I tried my best)*, this is what I came up with for the layout:
42 |
43 | ```swift
44 | let isLandscape : Bool = UIDevice.currentDevice().orientation.isLandscape.boolValue
45 | let bannerHeight : CGFloat = view.height() * 0.43
46 | let avatarHeightMultipler : CGFloat = isLandscape ? 0.75 : 0.43
47 | let avatarSize = bannerHeight * avatarHeightMultipler
48 |
49 | searchBar.fillSuperview()
50 | bannerImageView.anchorAndFillEdge(.Top, xPad: 0, yPad: 0, otherSize: bannerHeight)
51 | bannerMaskView.fillSuperview()
52 | avatarImageView.anchorInCorner(.BottomLeft, xPad: 15, yPad: 15, width: avatarSize, height: avatarSize)
53 | nameLabel.alignAndFillWidth(align: .ToTheRightCentered, relativeTo: avatarImageView, padding: 15, height: 120)
54 | cameraButton.anchorInCorner(.BottomRight, xPad: 10, yPad: 7, width: 28, height: 28)
55 | buttonContainerView.alignAndFillWidth(align: .UnderCentered, relativeTo: bannerImageView, padding: 0, height: 62)
56 | buttonContainerView.groupAndFill(group: .Horizontal, views: [postButton, updateInfoButton, activityLogButton, moreButton], padding: 10)
57 | buttonContainerView2.alignAndFillWidth(align: .UnderCentered, relativeTo: buttonContainerView, padding: 0, height: 128)
58 | buttonContainerView2.groupAndFill(group: .Horizontal, views: [aboutView, photosView, friendsView], padding: 10)
59 | ```
60 |
61 | 
62 |
63 | Looks pretty good on every device size! Now, keep in mind you'll probably want constants defined for many of these size/padding values, in order to keep the code cleaner and easier to maintain, but I decided to use real numbers for most of the values to make the code less obscure when new people are reading through the demonstration. Now, ***unlike Facebook's iPhone app*** the layout built with Neon is ***dynamic***. It is able to handle rotation on all-sized devices with no problem:
64 |
65 | 
66 |
67 | ###Not bad for 10 lines of code!
68 |
69 |
70 | Here's an intentionally convoluted example to show how easy it is to build very complex and dynamic layouts with Neon. The following layout was created with only *20 lines of code*. That's one line of code per view! While impressive, this layout is horrifying and should never be used in an actual app... ever...
71 |
72 | 
73 |
74 |
75 | ## Anchoring Views
76 |
77 | ### Center
78 |
79 | There are a few ways you can anchor views using Neon, and the first and most simple is anchoring a view in the center of its superview:
80 |
81 | ```swift
82 | view1.anchorInCenter(width: size, height: size)
83 | ```
84 |
85 | 
86 |
87 |
88 | ### Filling Superview
89 |
90 | Sometimes you want a view to fill its superview entirely, which couldn't be easier.
91 |
92 | ```swift
93 | view.fillSuperview()
94 | ```
95 |
96 | Optionally, if you want a view to fill its superview with padding, you can provide padding instead:
97 |
98 | ```swift
99 | view1.fillSuperview(left: padding, right: padding, top: padding, bottom: padding)
100 | ```
101 |
102 | 
103 |
104 |
105 | ### Corner
106 |
107 | The second anchoring method is anchoring a view in its superview's `Corner`. As you might have guessed, the four corners are `.TopLeft`, `.TopRight`, `.BottomLeft`, `.BottomRight`, and coupled with the `anchorInCorner()` function, you can easily anchor a view in any corner like this:
108 |
109 | ```swift
110 | view1.anchorInCorner(.TopLeft, xPad: padding, yPad: padding, width: size, height: size)
111 | view2.anchorInCorner(.TopRight, xPad: padding, yPad: padding, width: size, height: size)
112 | view3.anchorInCorner(.BottomLeft, xPad: padding, yPad: padding, width: size, height: size)
113 | view4.anchorInCorner(.BottomRight, xPad: padding, yPad: padding, width: size, height: size)
114 | ```
115 |
116 | 
117 |
118 |
119 | ### Edge
120 |
121 | `Edge` is another pretty obvious one to follow - it specifies on what edge of its superview a view will be anchored to. The four types are `.Top`, `.Left`, `.Bottom`, or `.Right`, and similar to previous examples, you can use the `anchorToEdge()` function to anchor a view to an edge:
122 |
123 | ```swift
124 | view1.anchorToEdge(.Top, padding: padding, width: size, height: size)
125 | view2.anchorToEdge(.Left, padding: padding, width: size, height: size)
126 | view3.anchorToEdge(.Bottom, padding: padding, width: size, height: size)
127 | view4.anchorToEdge(.Right, padding: padding, width: size, height: size)
128 | ```
129 |
130 | 
131 |
132 |
133 |
134 | ### Filling an edge
135 |
136 | Sometimes, you want to anchor a view against an edge, filling that edge; imagine something like a banner photo for a profile page. Again, this is made as simple as possible using the `anchorAndFillEdge()` function:
137 |
138 | ```swift
139 | view1.anchorAndFillEdge(.Top, xPad: padding, yPad: padding, otherSize: size)
140 | view2.anchorAndFillEdge(.Bottom, xPad: padding, yPad: padding, otherSize: size)
141 | view3.anchorAndFillEdge(.Left, xPad: padding, yPad: padding, otherSize: size)
142 | view4.anchorAndFillEdge(.Right, xPad: padding, yPad: padding, otherSize: size)
143 | ```
144 |
145 | 
146 |
147 |
148 | > Note that `anchorAndFillEdge()` accepts a parameter called `otherSize`. That parameter is used to set the *other size* that isn't automatically calculated by filling the edge, meaning that if you specify that you want to anchor to and fill the top edge, the width will be automatically calculated, but the height is still unknown, so the value passed in to `otherSize` will be used to set the height. Subsequently, if you want to anchor to and fill the left edge, the height is automatically calculated and `otherSize` will be used to set the width of the view.
149 | >
150 |
151 |
152 | ## Align
153 |
154 | Now that we've anchored primary views, we can start making our UI more complex by aligning other views *relative to other sibling views*, using the (you guessed it) `Align` value. **Sibling views** are views that share the same superview directly. There are twelve `Align` types, and they are all pretty self-explanatory - here's an example using all twelve with the `align()` function:
155 |
156 | ```
157 | view1.align(.AboveMatchingLeft, relativeTo: anchorView, padding: padding, width: size, height: size)
158 | view2.align(.AboveCentered, relativeTo: anchorView, padding: padding, width: size, height: size)
159 | view3.align(.AboveMatchingRight, relativeTo: anchorView, padding: padding, width: size, height: size)
160 | view4.align(.ToTheRightMatchingTop, relativeTo: anchorView, padding: padding, width: size, height: size)
161 | view5.align(.ToTheRightCentered, relativeTo: anchorView, padding: padding, width: size, height: size)
162 | view6.align(.ToTheRightMatchingBottom, relativeTo: anchorView, padding: padding, width: size, height: size)
163 | view7.align(.UnderMatchingRight, relativeTo: anchorView, padding: padding, width: size, height: size)
164 | view8.align(.UnderCentered, relativeTo: anchorView, padding: padding, width: size, height: size)
165 | view9.align(.UnderMatchingLeft, relativeTo: anchorView, padding: padding, width: size, height: size)
166 | view10.align(.ToTheLeftMatchingBottom, relativeTo: anchorView, padding: padding, width: size, height: size)
167 | view11.align(.ToTheLeftCentered, relativeTo: anchorView, padding: padding, width: size, height: size)
168 | view12.align(.ToTheLeftMatchingTop, relativeTo: anchorView, padding: padding, width: size, height: size)
169 | ```
170 |
171 | 
172 |
173 |
174 | ## Align and fill
175 |
176 | You don't always know or want to specify the size of a view that you want to layout relative to another, but rather you want to either fill the width, height, or the entire rest of the superview, after aligning with the sibling. Combined with all the different alignment types discussed earlier, we're starting to see how more complex layouts can be built very easily:
177 |
178 | ```swift
179 | view2.alignAndFillWidth(align: .ToTheRightMatchingTop, relativeTo: view1, padding: padding, height: size / 2.0)
180 | view4.alignAndFillHeight(align: .AboveCentered, relativeTo: view3, padding: padding, width: size / 2.0)
181 | view6.alignAndFill(align: .ToTheLeftMatchingTop, relativeTo: view5, padding: padding)
182 | ```
183 |
184 | 
185 |
186 |
187 | ## Align between
188 |
189 | Sometimes you want a view to sit between to other views, filling the space between them. Using alignBetweenHorizontal() and alignBetweenVertical(), doing that is super easy! Choose one of your sibling views you want to align your view relative to and pass that in as your `primaryView`. We will use the specified `align` parameter to match that `primaryView` appropriately, and automatically fill either the horizontal or vertical span between the it and the `secondaryView`.
190 |
191 | ```swift
192 | view1.alignBetweenHorizontal(align: .ToTheRightMatchingTop, primaryView: anchorViewA, secondaryView: anchorViewB, padding: padding, height: size)
193 | view2.alignBetweenVertical(align: .UnderCentered, primaryView: anchorViewB, secondaryView: anchorViewD, padding: padding, width: size)
194 | view3.alignBetweenHorizontal(align: .ToTheLeftMatchingBottom, primaryView: anchorViewD, secondaryView: anchorViewC, padding: padding, height: size)
195 | view4.alignBetweenVertical(align: .AboveMatchingRight, primaryView: anchorViewC, secondaryView: anchorViewA, padding: padding, width: size)
196 | ```
197 |
198 | 
199 |
200 | ## What about labels?
201 |
202 | One of the more complicated parts of working with dynamic layouts, is dealing with labels that may have 1 -> n lines, and as such passing in a specific height isn't always possible without causing a migraine. Neon makes this easy by introducing the `AutoHeight` constant. Pass this value into methods that accept a `height` param, and we will first set the width of the frame, tell the view to `sizeToFit()` so the height is automatically set based on its contents, and then align the view appropriately. For example:
203 |
204 | ```swift
205 | testLabel.alignBetweenHorizontal(align: .ToTheRightMatchingBottom, primaryView: anchorViewA, secondaryView: anchorViewB, padding: padding, height: AutoHeight)
206 | ```
207 |
208 | 
209 |
210 | Note that changing the text to something with more characters still produces the same desired result:
211 |
212 | 
213 |
214 | > It's important to note that the using `AutoHeight` with something like a `CALayer`, or passing it in to any of the grouping methods (see below) will have undesired consequences, as it almost doesn't *make sense* in this context. Use `AutoHeight` with anything that implements `sizeToFit()` and you should be OK. The vast majority of cases where you'll want to use this is with `UILabel` objects.
215 |
216 | ## What if I don't want to align them perfectly?
217 |
218 | Sometimes you don't want your views to align with their sibling views *exactly* - you may want to align them relative to their siblings, but with a slight offset. You can do this by adding the optional `offset` parameter to any of the above align methods to produce something like the following:
219 |
220 | ```swift
221 | view1.align(.ToTheRightMatchingTop, relativeTo: anchorViewA, padding: padding, width: size, height: size, offset: offset)
222 | view2.align(.UnderMatchingLeft, relativeTo: anchorViewA, padding: padding, width: size, height: size, offset: offset)
223 | ```
224 |
225 | 
226 |
227 | ## Grouping
228 |
229 | Another common use-case is the *grouping* of sibling views, aligned in a row or column. Using what we've already learned about anchoring views in the center, in a corner, or against an edge, we can also do the same with groups of views!
230 |
231 | The primary difference with grouping, is that it is done by the *parent view,* or `superview` of a group of views. For example, let's let two different views center a group of their subviews in each of the two different `Group` configurations, `.Horizontal` and `.Vertical`:
232 |
233 | ```swift
234 | anchorViewA.groupInCenter(group: .Horizontal, views: [view1, view2, view3], padding: padding, width: size, height: size)
235 | anchorViewB.groupInCenter(group: .Vertical, views: [view4, view5, view6], padding: padding, width: size, height: size)
236 | ```
237 |
238 | 
239 |
240 | How about grouping views in the corner?
241 |
242 | ```swift
243 | anchorViewA.groupInCorner(group: .Horizontal, views: [view1, view2, view3], inCorner: .TopLeft, padding: padding, width: size, height: size)
244 | anchorViewB.groupInCorner(group: .Vertical, views: [view4, view5, view6], inCorner: .BottomRight, padding: padding, width: size, height: size)
245 | ```
246 |
247 | 
248 |
249 | As you might have expected, you can also group either horizontally and vertically against any edge as well:
250 |
251 | ```swift
252 | anchorViewA.groupAgainstEdge(group: .Horizontal, views: [view1, view2, view3], againstEdge: .Left, padding: padding, width: size, height: size)
253 | anchorViewB.groupAgainstEdge(group: .Vertical, views: [view4, view5, view6], againstEdge: .Bottom, padding: padding, width: size, height: size)
254 | ```
255 |
256 | 
257 |
258 | Grouping views relative to a sibling view can be done as well:
259 |
260 | ```swift
261 | view.groupAndAlign(group: .Horizontal, andAlign: .ToTheRightMatchingTop, views: [view1, view2, view3], relativeTo: anchorViewA, padding: padding, width: size, height: size)
262 | view.groupAndAlign(group: .Vertical, andAlign: .UnderCentered, views: [view4, view5, view6], relativeTo: anchorViewA, padding: padding, width: size, height: size)
263 | ```
264 |
265 | 
266 |
267 | You can also specify that you want a group of subviews to fill their superview, either horizontally or vertically:
268 |
269 | ```swift
270 | anchorViewA.groupAndFill(group: .Horizontal, views: [view1, view2, view3], padding: padding)
271 | anchorViewB.groupAndFill(group: .Vertical, views: [view4, view5, view6], padding: padding)
272 | ```
273 | 
274 |
275 | ## License
276 |
277 | The source is made available under the MIT license. See LICENSE.txt for details.
278 |
--------------------------------------------------------------------------------
/Screenshots/align.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/align.png
--------------------------------------------------------------------------------
/Screenshots/align_between_fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/align_between_fill.png
--------------------------------------------------------------------------------
/Screenshots/align_fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/align_fill.png
--------------------------------------------------------------------------------
/Screenshots/align_offset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/align_offset.png
--------------------------------------------------------------------------------
/Screenshots/auto_height_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/auto_height_1.png
--------------------------------------------------------------------------------
/Screenshots/auto_height_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/auto_height_2.png
--------------------------------------------------------------------------------
/Screenshots/center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/center.png
--------------------------------------------------------------------------------
/Screenshots/corner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/corner.png
--------------------------------------------------------------------------------
/Screenshots/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/demo.gif
--------------------------------------------------------------------------------
/Screenshots/edge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/edge.png
--------------------------------------------------------------------------------
/Screenshots/fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/fill.png
--------------------------------------------------------------------------------
/Screenshots/fill_edge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/fill_edge.png
--------------------------------------------------------------------------------
/Screenshots/group_against_edge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/group_against_edge.png
--------------------------------------------------------------------------------
/Screenshots/group_and_fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/group_and_fill.png
--------------------------------------------------------------------------------
/Screenshots/group_in_center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/group_in_center.png
--------------------------------------------------------------------------------
/Screenshots/group_in_corner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/group_in_corner.png
--------------------------------------------------------------------------------
/Screenshots/group_relative.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/group_relative.png
--------------------------------------------------------------------------------
/Screenshots/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/landscape.png
--------------------------------------------------------------------------------
/Screenshots/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/logo.png
--------------------------------------------------------------------------------
/Screenshots/portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/portrait.png
--------------------------------------------------------------------------------
/Screenshots/side_by_side.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mamaral/Neon/d11e6332bce5317b548b14c38cd3600643402512/Screenshots/side_by_side.png
--------------------------------------------------------------------------------
/Source/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSHumanReadableCopyright
24 | Copyright © 2015 Mike Amaral. All rights reserved.
25 | NSPrincipalClass
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Source/Neon.h:
--------------------------------------------------------------------------------
1 | //
2 | // Neon.h
3 | // Neon
4 | //
5 | // Created by Sean Cheng on 10/3/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 | //! Project version number for Neon-iOS.
13 | FOUNDATION_EXPORT double NeonVersionNumber;
14 |
15 | //! Project version string for Neon-iOS.
16 | FOUNDATION_EXPORT const unsigned char NeonVersionString[];
17 |
18 | // In this header, you should import all the public headers of your framework using statements like #import
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Source/Neon.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Neon.swift
3 | // Neon
4 | //
5 | // Created by Mike on 9/16/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import UIKit
11 | #else
12 | import Cocoa
13 | #endif
14 |
15 |
16 | // MARK: AutoHeight
17 | //
18 | ///
19 | /// `CGFloat` constant used to specify that you want the height to be automatically calculated
20 | /// using `sizeToFit()`.
21 | ///
22 | public let AutoHeight : CGFloat = -1
23 | public let AutoWidth : CGFloat = -1
24 |
25 |
26 | // MARK: Corner
27 | //
28 | ///
29 | /// Specifies a corner of a frame.
30 | ///
31 | /// **topLeft**: The upper-left corner of the frame.
32 | ///
33 | /// **topRight**: The upper-right corner of the frame.
34 | ///
35 | /// **bottomLeft**: The bottom-left corner of the frame.
36 | ///
37 | /// **bottomRight**: The upper-right corner of the frame.
38 | ///
39 | public enum Corner {
40 | case topLeft
41 | case topRight
42 | case bottomLeft
43 | case bottomRight
44 | }
45 |
46 |
47 | // MARK: Edge
48 | //
49 | ///
50 | /// Specifies an edge, or face, of a frame.
51 | ///
52 | /// **top**: The top edge of the frame.
53 | ///
54 | /// **left**: The left edge of the frame.
55 | ///
56 | /// **bottom**: The bottom edge of the frame.
57 | ///
58 | /// **right**: The right edge of the frame.
59 | ///
60 | public enum Edge {
61 | case top
62 | case left
63 | case bottom
64 | case right
65 | }
66 |
67 |
68 | // MARK: Align Type
69 | //
70 | ///
71 | /// Specifies how a view will be aligned relative to the sibling view.
72 | ///
73 | /// **toTheRightMatchingTop**: Specifies that the view should be aligned to the right of a sibling, matching the
74 | /// top, or y origin, of the sibling's frame.
75 | ///
76 | /// **toTheRightMatchingBottom**: Specifies that the view should be aligned to the right of a sibling, matching
77 | /// the bottom, or max y value, of the sibling's frame.
78 | ///
79 | /// **toTheRightCentered**: Specifies that the view should be aligned to the right of a sibling, and will be centered
80 | /// to either match the vertical center of the sibling's frame or centered vertically within the superview, depending
81 | /// on the context.
82 | ///
83 | /// **toTheLeftMatchingTop**: Specifies that the view should be aligned to the left of a sibling, matching the top,
84 | /// or y origin, of the sibling's frame.
85 | ///
86 | /// **toTheLeftMatchingBottom**: Specifies that the view should be aligned to the left of a sibling, matching the
87 | /// bottom, or max y value, of the sibling's frame.
88 | ///
89 | /// **toTheLeftCentered**: Specifies that the view should be aligned to the left of a sibling, and will be centered
90 | /// to either match the vertical center of the sibling's frame or centered vertically within the superview, depending
91 | /// on the context.
92 | ///
93 | /// **underMatchingLeft**: Specifies that the view should be aligned under a sibling, matching the left, or x origin,
94 | /// of the sibling's frame.
95 | ///
96 | /// **underMatchingRight**: Specifies that the view should be aligned under a sibling, matching the right, or max y
97 | /// of the sibling's frame.
98 | ///
99 | /// **underCentered**: Specifies that the view should be aligned under a sibling, and will be centered to either match
100 | /// the horizontal center of the sibling's frame or centered horizontally within the superview, depending on the context.
101 | ///
102 | /// **aboveMatchingLeft**: Specifies that the view should be aligned above a sibling, matching the left, or x origin
103 | /// of the sibling's frame.
104 | ///
105 | /// **aboveMatchingRight**: Specifies that the view should be aligned above a sibling, matching the right, or max x
106 | /// of the sibling's frame.
107 | ///
108 | /// **aboveCentered**: Specifies that the view should be aligned above a sibling, and will be centered to either match
109 | /// the horizontal center of the sibling's frame or centered horizontally within the superview, depending on the context.
110 | ///
111 | public enum Align {
112 | case toTheRightMatchingTop
113 | case toTheRightMatchingBottom
114 | case toTheRightCentered
115 | case toTheLeftMatchingTop
116 | case toTheLeftMatchingBottom
117 | case toTheLeftCentered
118 | case underMatchingLeft
119 | case underMatchingRight
120 | case underCentered
121 | case aboveMatchingLeft
122 | case aboveMatchingRight
123 | case aboveCentered
124 | }
125 |
126 |
127 | // MARK: Group Type
128 | //
129 | ///
130 | /// Specifies how a group will be laid out.
131 | ///
132 | /// **horizontal**: Specifies that the views should be aligned relative to eachother horizontally.
133 | ///
134 | /// **vertical**: Specifies that the views should be aligned relative to eachother vertically.
135 | ///
136 | public enum Group {
137 | case horizontal
138 | case vertical
139 | }
140 |
--------------------------------------------------------------------------------
/Source/NeonAlignable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NeonAlignable.swift
3 | // Neon
4 | //
5 | // Created by Mike on 10/1/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import UIKit
11 | #else
12 | import Cocoa
13 | #endif
14 |
15 |
16 | public protocol Alignable : Frameable {}
17 |
18 | public extension Alignable {
19 |
20 | /// Align a view relative to a sibling view in the same superview.
21 | ///
22 | /// - parameters:
23 | /// - align: The `Align` type used to specify where and how this view is aligned with its sibling.
24 | ///
25 | /// - relativeTo: The sibling view this view will be aligned relative to. **NOTE:** Ensure this sibling view shares
26 | /// the same superview as this view, and that the sibling view is not the same as this view, otherwise a
27 | /// `fatalError` is thrown.
28 | ///
29 | /// - padding: The padding to be applied between this view and the sibling view, which is applied differently
30 | /// depending on the `Align` specified. For example, if aligning `.ToTheRightOfMatchingTop` the padding is used
31 | /// to adjust the x origin of this view so it sits to the right of the sibling view, while the y origin is
32 | /// automatically calculated to match the sibling view.
33 | ///
34 | /// - width: The width of the view.
35 | ///
36 | /// - height: The height of the view.
37 | ///
38 | /// - offset: An optional parameter that will offset the view by the defined amount such that the view will not perfectly
39 | /// match the specified `Align`. For example, if you specify `.ToTheRightMatchingTop` and provide an offset value of `5`, the
40 | /// view's y origin will be lower than the sibling view's y origin by 5 points.
41 | ///
42 | public func align(_ align: Align, relativeTo sibling: Frameable, padding: CGFloat, width: CGFloat, height: CGFloat, offset: CGFloat = 0) {
43 | var xOrigin : CGFloat = 0.0
44 | var yOrigin : CGFloat = 0.0
45 |
46 | switch align {
47 | case .toTheRightMatchingTop:
48 | xOrigin = sibling.xMax + padding
49 | yOrigin = sibling.y + offset
50 |
51 | case .toTheRightMatchingBottom:
52 | xOrigin = sibling.xMax + padding
53 | yOrigin = sibling.yMax - height + offset
54 |
55 | case .toTheRightCentered:
56 | xOrigin = sibling.xMax + padding
57 | yOrigin = sibling.yMid - (height / 2.0) + offset
58 |
59 | case .toTheLeftMatchingTop:
60 | xOrigin = sibling.x - width - padding
61 | yOrigin = sibling.y + offset
62 |
63 | case .toTheLeftMatchingBottom:
64 | xOrigin = sibling.x - width - padding
65 | yOrigin = sibling.yMax - height + offset
66 |
67 | case .toTheLeftCentered:
68 | xOrigin = sibling.x - width - padding
69 | yOrigin = sibling.yMid - (height / 2.0) + offset
70 |
71 | case .underMatchingLeft:
72 | xOrigin = sibling.x + offset
73 | yOrigin = sibling.yMax + padding
74 |
75 | case .underMatchingRight:
76 | xOrigin = sibling.xMax - width + offset
77 | yOrigin = sibling.yMax + padding
78 |
79 | case .underCentered:
80 | xOrigin = sibling.xMid - (width / 2.0) + offset
81 | yOrigin = sibling.yMax + padding
82 |
83 | case .aboveMatchingLeft:
84 | xOrigin = sibling.x + offset
85 | yOrigin = sibling.y - padding - height
86 |
87 | case .aboveMatchingRight:
88 | xOrigin = sibling.xMax - width + offset
89 | yOrigin = sibling.y - padding - height
90 |
91 | case .aboveCentered:
92 | xOrigin = sibling.xMid - (width / 2.0) + offset
93 | yOrigin = sibling.y - padding - height
94 | }
95 |
96 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
97 |
98 | if height == AutoHeight {
99 | self.setDimensionAutomatically()
100 | self.align(align, relativeTo: sibling, padding: padding, width: width, height: self.height, offset: offset)
101 | }
102 | if width == AutoWidth {
103 | self.setDimensionAutomatically()
104 | self.align(align, relativeTo: sibling, padding: padding, width: self.width, height: height, offset: offset)
105 | }
106 | }
107 |
108 |
109 |
110 | /// Align a view relative to a sibling view in the same superview, and automatically expand the width to fill
111 | /// the superview with equal padding between the superview and sibling view.
112 | ///
113 | /// - parameters:
114 | /// - align: The `Align` type used to specify where and how this view is aligned with its sibling.
115 | ///
116 | /// - relativeTo: The sibling view this view will be aligned relative to. **NOTE:** Ensure this sibling view shares
117 | /// the same superview as this view, and that the sibling view is not the same as this view, otherwise a
118 | /// `fatalError` is thrown.
119 | ///
120 | /// - padding: The padding to be applied between this view, the sibling view and the superview.
121 | ///
122 | /// - height: The height of the view.
123 | ///
124 | /// - offset: An optional parameter that will offset the view by the defined amount such that the view will not perfectly
125 | /// match the specified `Align`. For example, if you specify `.ToTheRightMatchingTop` and provide an offset value of `5`, the
126 | /// view's y origin will be lower than the sibling view's y origin by 5 points.
127 | ///
128 | public func alignAndFillWidth(align: Align, relativeTo sibling: Frameable, padding: CGFloat, height: CGFloat, offset: CGFloat = 0) {
129 | let superviewWidth = superFrame.width
130 | var xOrigin : CGFloat = 0.0
131 | var yOrigin : CGFloat = 0.0
132 | var width : CGFloat = 0.0
133 |
134 | switch align {
135 | case .toTheRightMatchingTop:
136 | xOrigin = sibling.xMax + padding
137 | yOrigin = sibling.y + offset
138 | width = superviewWidth - xOrigin - padding
139 |
140 | case .toTheRightMatchingBottom:
141 | xOrigin = sibling.xMax + padding
142 | yOrigin = sibling.yMax - height + offset
143 | width = superviewWidth - xOrigin - padding
144 |
145 | case .toTheRightCentered:
146 | xOrigin = sibling.xMax + padding
147 | yOrigin = sibling.yMid - (height / 2.0) + offset
148 | width = superviewWidth - xOrigin - padding
149 |
150 | case .toTheLeftMatchingTop:
151 | xOrigin = padding
152 | yOrigin = sibling.y + offset
153 | width = sibling.x - (2 * padding)
154 |
155 | case .toTheLeftMatchingBottom:
156 | xOrigin = padding
157 | yOrigin = sibling.yMax - height + offset
158 | width = sibling.x - (2 * padding)
159 |
160 | case .toTheLeftCentered:
161 | xOrigin = padding
162 | yOrigin = sibling.yMid - (height / 2.0) + offset
163 | width = sibling.x - (2 * padding)
164 |
165 | case .underMatchingLeft:
166 | xOrigin = sibling.x + offset
167 | yOrigin = sibling.yMax + padding
168 | width = superviewWidth - xOrigin - padding
169 |
170 | case .underMatchingRight:
171 | xOrigin = padding + offset
172 | yOrigin = sibling.yMax + padding
173 | width = superviewWidth - (superviewWidth - sibling.xMax) - padding
174 |
175 | case .underCentered:
176 | xOrigin = padding + offset
177 | yOrigin = sibling.yMax + padding
178 | width = superviewWidth - (2 * padding)
179 |
180 | case .aboveMatchingLeft:
181 | xOrigin = sibling.x + offset
182 | yOrigin = sibling.y - padding - height
183 | width = superviewWidth - xOrigin - padding
184 |
185 | case .aboveMatchingRight:
186 | xOrigin = padding + offset
187 | yOrigin = sibling.y - padding - height
188 | width = superviewWidth - (superviewWidth - sibling.xMax) - padding
189 |
190 | case .aboveCentered:
191 | xOrigin = padding + offset
192 | yOrigin = sibling.y - padding - height
193 | width = superviewWidth - (2 * padding)
194 | }
195 |
196 | if width < 0.0 {
197 | width = 0.0
198 | }
199 |
200 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
201 |
202 | if height == AutoHeight {
203 | self.setDimensionAutomatically()
204 | self.alignAndFillWidth(align: align, relativeTo: sibling, padding: padding, height: self.height, offset: offset)
205 | }
206 | }
207 |
208 |
209 | /// Align a view relative to a sibling view in the same superview, and automatically expand the height to fill
210 | /// the superview with equal padding between the superview and sibling view.
211 | ///
212 | /// - parameters:
213 | /// - align: The `Align` type used to specify where and how this view is aligned with its sibling.
214 | ///
215 | /// - relativeTo: The sibling view this view will be aligned relative to. **NOTE:** Ensure this sibling view shares
216 | /// the same superview as this view, and that the sibling view is not the same as this view, otherwise a
217 | /// `fatalError` is thrown.
218 | ///
219 | /// - padding: The padding to be applied between this view, the sibling view and the superview.
220 | ///
221 | /// - width: The width of the view.
222 | ///
223 | /// - offset: An optional parameter that will offset the view by the defined amount such that the view will not perfectly
224 | /// match the specified `Align`. For example, if you specify `.ToTheRightMatchingTop` and provide an offset value of `5`, the
225 | /// view's y origin will be lower than the sibling view's y origin by 5 points.
226 | ///
227 | public func alignAndFillHeight(align: Align, relativeTo sibling: Frameable, padding: CGFloat, width: CGFloat, offset: CGFloat = 0) {
228 | let superviewHeight : CGFloat = superFrame.height
229 | var xOrigin : CGFloat = 0.0
230 | var yOrigin : CGFloat = 0.0
231 | var height : CGFloat = 0.0
232 |
233 | switch align {
234 | case .toTheRightMatchingTop:
235 | xOrigin = sibling.xMax + padding
236 | yOrigin = sibling.y + offset
237 | height = superviewHeight - sibling.y - padding
238 |
239 | case .toTheRightMatchingBottom:
240 | xOrigin = sibling.xMax + padding
241 | yOrigin = padding + offset
242 | height = superviewHeight - (superviewHeight - sibling.yMax) - padding
243 |
244 | case .toTheRightCentered:
245 | xOrigin = sibling.xMax + padding
246 | yOrigin = padding + offset
247 | height = superviewHeight - (2 * padding)
248 |
249 | case .toTheLeftMatchingTop:
250 | xOrigin = sibling.x - width - padding
251 | yOrigin = sibling.y + offset
252 | height = superviewHeight - sibling.y - padding
253 |
254 | case .toTheLeftMatchingBottom:
255 | xOrigin = sibling.x - width - padding
256 | yOrigin = padding + offset
257 | height = superviewHeight - (superviewHeight - sibling.yMax) - padding
258 |
259 | case .toTheLeftCentered:
260 | xOrigin = sibling.x - width - padding
261 | yOrigin = padding + offset
262 | height = superviewHeight - (2 * padding)
263 |
264 | case .underMatchingLeft:
265 | xOrigin = sibling.x + offset
266 | yOrigin = sibling.yMax + padding
267 | height = superviewHeight - yOrigin - padding
268 |
269 | case .underMatchingRight:
270 | xOrigin = sibling.xMax - width + offset
271 | yOrigin = sibling.yMax + padding
272 | height = superviewHeight - yOrigin - padding
273 |
274 | case .underCentered:
275 | xOrigin = sibling.xMid - (width / 2.0) + offset
276 | yOrigin = sibling.yMax + padding
277 | height = superviewHeight - yOrigin - padding
278 |
279 | case .aboveMatchingLeft:
280 | xOrigin = sibling.x + offset
281 | yOrigin = padding
282 | height = sibling.y - (2 * padding)
283 |
284 | case .aboveMatchingRight:
285 | xOrigin = sibling.xMax - width + offset
286 | yOrigin = padding
287 | height = sibling.y - (2 * padding)
288 |
289 | case .aboveCentered:
290 | xOrigin = sibling.xMid - (width / 2.0) + offset
291 | yOrigin = padding
292 | height = sibling.y - (2 * padding)
293 | }
294 |
295 | if height < 0.0 {
296 | height = 0.0
297 | }
298 |
299 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
300 |
301 | if height == AutoHeight {
302 | self.setDimensionAutomatically()
303 | self.alignAndFillHeight(align: align, relativeTo: sibling, padding: padding, width: self.height, offset: offset)
304 | }
305 | }
306 |
307 |
308 | /// Align a view relative to a sibling view in the same superview, and automatically expand the width AND height
309 | /// to fill the superview with equal padding between the superview and sibling view.
310 | ///
311 | /// - parameters:
312 | /// - align: The `Align` type used to specify where and how this view is aligned with its sibling.
313 | ///
314 | /// - relativeTo: The sibling view this view will be aligned relative to. **NOTE:** Ensure this sibling view shares
315 | /// the same superview as this view, and that the sibling view is not the same as this view, otherwise a
316 | /// `fatalError` is thrown.
317 | ///
318 | /// - padding: The padding to be applied between this view, the sibling view and the superview.
319 | ///
320 | /// - offset: An optional parameter that will offset the view by the defined amount such that the view will not perfectly
321 | /// match the specified `Align`. For example, if you specify `.ToTheRightMatchingTop` and provide an offset value of `5`, the
322 | /// view's y origin will be lower than the sibling view's y origin by 5 points.
323 | ///
324 | public func alignAndFill(align: Align, relativeTo sibling: Frameable, padding: CGFloat, offset: CGFloat = 0) {
325 | let superviewWidth : CGFloat = superFrame.width
326 | let superviewHeight : CGFloat = superFrame.height
327 | var xOrigin : CGFloat = 0.0
328 | var yOrigin : CGFloat = 0.0
329 | var width : CGFloat = 0.0
330 | var height : CGFloat = 0.0
331 |
332 | switch align {
333 | case .toTheRightMatchingTop:
334 | xOrigin = sibling.xMax + padding
335 | yOrigin = sibling.y + offset
336 | width = superviewWidth - xOrigin - padding
337 | height = superviewHeight - yOrigin - padding
338 |
339 | case .toTheRightMatchingBottom:
340 | xOrigin = sibling.xMax + padding
341 | yOrigin = padding + offset
342 | width = superviewWidth - xOrigin - padding
343 | height = superviewHeight - (superviewHeight - sibling.yMax) - padding
344 |
345 | case .toTheRightCentered:
346 | xOrigin = sibling.xMax + padding
347 | yOrigin = padding + offset
348 | width = superviewWidth - xOrigin - padding
349 | height = superviewHeight - (2 * padding)
350 |
351 | case .toTheLeftMatchingTop:
352 | xOrigin = padding
353 | yOrigin = sibling.y + offset
354 | width = superviewWidth - (superviewWidth - sibling.x) - (2 * padding)
355 | height = superviewHeight - yOrigin - padding
356 |
357 | case .toTheLeftMatchingBottom:
358 | xOrigin = padding
359 | yOrigin = padding + offset
360 | width = superviewWidth - (superviewWidth - sibling.x) - (2 * padding)
361 | height = superviewHeight - (superviewHeight - sibling.yMax) - padding
362 |
363 | case .toTheLeftCentered:
364 | xOrigin = padding
365 | yOrigin = padding + offset
366 | width = superviewWidth - (superviewWidth - sibling.x) - (2 * padding)
367 | height = superviewHeight - (2 * padding)
368 |
369 | case .underMatchingLeft:
370 | xOrigin = sibling.x + offset
371 | yOrigin = sibling.yMax + padding
372 | width = superviewWidth - xOrigin - padding
373 | height = superviewHeight - yOrigin - padding
374 |
375 | case .underMatchingRight:
376 | xOrigin = padding + offset
377 | yOrigin = sibling.yMax + padding
378 | width = superviewWidth - (superviewWidth - sibling.xMax) - padding
379 | height = superviewHeight - yOrigin - padding
380 |
381 | case .underCentered:
382 | xOrigin = padding + offset
383 | yOrigin = sibling.yMax + padding
384 | width = superviewWidth - (2 * padding)
385 | height = superviewHeight - yOrigin - padding
386 |
387 | case .aboveMatchingLeft:
388 | xOrigin = sibling.x + offset
389 | yOrigin = padding
390 | width = superviewWidth - xOrigin - padding
391 | height = superviewHeight - (superviewHeight - sibling.y) - (2 * padding)
392 |
393 | case .aboveMatchingRight:
394 | xOrigin = padding + offset
395 | yOrigin = padding
396 | width = superviewWidth - (superviewWidth - sibling.xMax) - padding
397 | height = superviewHeight - (superviewHeight - sibling.y) - (2 * padding)
398 |
399 | case .aboveCentered:
400 | xOrigin = padding + offset
401 | yOrigin = padding
402 | width = superviewWidth - (2 * padding)
403 | height = superviewHeight - (superviewHeight - sibling.y) - (2 * padding)
404 | }
405 |
406 | if width < 0.0 {
407 | width = 0.0
408 | }
409 |
410 | if height < 0.0 {
411 | height = 0.0
412 | }
413 |
414 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
415 | }
416 |
417 |
418 | /// Align a view between two sibling views horizontally, automatically expanding the width to extend the full
419 | /// horizontal span between the `primaryView` and the `secondaryView`, with equal padding on both sides.
420 | ///
421 | /// - parameters:
422 | /// - align: The `Align` type used to specify where and how this view is aligned with the primary view.
423 | ///
424 | /// - primaryView: The primary sibling view this view will be aligned relative to.
425 | ///
426 | /// - secondaryView: The secondary sibling view this view will be automatically sized to fill the space between.
427 | ///
428 | /// - padding: The horizontal padding to be applied between this view and both sibling views.
429 | ///
430 | /// - height: The height of the view.
431 | ///
432 | /// - offset: An optional parameter that will offset the view by the defined amount such that the view will not perfectly
433 | /// match the specified `Align`. For example, if you specify `.ToTheRightMatchingTop` and provide an offset value of `5`, the
434 | /// view's y origin will be lower than the sibling view's y origin by 5 points.
435 | ///
436 | public func alignBetweenHorizontal(align: Align, primaryView: Frameable, secondaryView: Frameable, padding: CGFloat, height: CGFloat, offset: CGFloat = 0) {
437 | let superviewWidth : CGFloat = superFrame.width
438 | var xOrigin : CGFloat = 0.0
439 | var yOrigin : CGFloat = 0.0
440 | var width : CGFloat = 0.0
441 |
442 | switch align {
443 | case .toTheRightMatchingTop:
444 | xOrigin = primaryView.xMax + padding
445 | yOrigin = primaryView.y + offset
446 | width = superviewWidth - primaryView.xMax - (superviewWidth - secondaryView.x) - (2 * padding)
447 |
448 | case .toTheRightMatchingBottom:
449 | xOrigin = primaryView.xMax + padding
450 | yOrigin = primaryView.yMax - height + offset
451 | width = superviewWidth - primaryView.xMax - (superviewWidth - secondaryView.x) - (2 * padding)
452 |
453 | case .toTheRightCentered:
454 | xOrigin = primaryView.xMax + padding
455 | yOrigin = primaryView.yMid - (height / 2.0) + offset
456 | width = superviewWidth - primaryView.xMax - (superviewWidth - secondaryView.x) - (2 * padding)
457 |
458 | case .toTheLeftMatchingTop:
459 | xOrigin = secondaryView.xMax + padding
460 | yOrigin = primaryView.y + offset
461 | width = superviewWidth - secondaryView.xMax - (superviewWidth - primaryView.x) - (2 * padding)
462 |
463 | case .toTheLeftMatchingBottom:
464 | xOrigin = secondaryView.xMax + padding
465 | yOrigin = primaryView.yMax - height + offset
466 | width = superviewWidth - secondaryView.xMax - (superviewWidth - primaryView.x) - (2 * padding)
467 |
468 | case .toTheLeftCentered:
469 | xOrigin = secondaryView.xMax + padding
470 | yOrigin = primaryView.yMid - (height / 2.0) + offset
471 | width = superviewWidth - secondaryView.xMax - (superviewWidth - primaryView.x) - (2 * padding)
472 |
473 | case .underMatchingLeft, .underMatchingRight, .underCentered, .aboveMatchingLeft, .aboveMatchingRight, .aboveCentered:
474 | fatalError("[NEON] Invalid Align specified for alignBetweenHorizonal().")
475 | }
476 |
477 | if width < 0.0 {
478 | width = 0.0
479 | }
480 |
481 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
482 |
483 | if height == AutoHeight {
484 | self.setDimensionAutomatically()
485 | self.alignBetweenHorizontal(align: align, primaryView: primaryView, secondaryView: secondaryView, padding: padding, height: self.height)
486 | }
487 | }
488 |
489 |
490 | /// Align a view between two sibling views vertically, automatically expanding the height to extend the full
491 | /// vertical span between the `primaryView` and the `secondaryView`, with equal padding above and below.
492 | ///
493 | /// - parameters:
494 | /// - align: The `Align` type used to specify where and how this view is aligned with the primary view.
495 | ///
496 | /// - primaryView: The primary sibling view this view will be aligned relative to.
497 | ///
498 | /// - secondaryView: The secondary sibling view this view will be automatically sized to fill the space between.
499 | ///
500 | /// - padding: The horizontal padding to be applied between this view and both sibling views.
501 | ///
502 | /// - width: The width of the view.
503 | ///
504 | /// - offset: An optional parameter that will offset the view by the defined amount such that the view will not perfectly
505 | /// match the specified `Align`. For example, if you specify `.ToTheRightMatchingTop` and provide an offset value of `5`, the
506 | /// view's y origin will be lower than the sibling view's y origin by 5 points.
507 | ///
508 | public func alignBetweenVertical(align: Align, primaryView: Frameable, secondaryView: Frameable, padding: CGFloat, width: CGFloat, offset: CGFloat = 0) {
509 | let superviewHeight : CGFloat = superFrame.height
510 | var xOrigin : CGFloat = 0.0
511 | var yOrigin : CGFloat = 0.0
512 | var height : CGFloat = 0.0
513 |
514 | switch align {
515 | case .underMatchingLeft:
516 | xOrigin = primaryView.x + offset
517 | yOrigin = primaryView.yMax + padding
518 | height = superviewHeight - primaryView.yMax - (superviewHeight - secondaryView.y) - (2 * padding)
519 |
520 | case .underMatchingRight:
521 | xOrigin = primaryView.xMax - width + offset
522 | yOrigin = primaryView.yMax + padding
523 | height = superviewHeight - primaryView.yMax - (superviewHeight - secondaryView.y) - (2 * padding)
524 |
525 | case .underCentered:
526 | xOrigin = primaryView.xMid - (width / 2.0) + offset
527 | yOrigin = primaryView.yMax + padding
528 | height = superviewHeight - primaryView.yMax - (superviewHeight - secondaryView.y) - (2 * padding)
529 |
530 | case .aboveMatchingLeft:
531 | xOrigin = primaryView.x + offset
532 | yOrigin = secondaryView.yMax + padding
533 | height = superviewHeight - secondaryView.yMax - (superviewHeight - primaryView.y) - (2 * padding)
534 |
535 | case .aboveMatchingRight:
536 | xOrigin = primaryView.xMax - width + offset
537 | yOrigin = secondaryView.yMax + padding
538 | height = superviewHeight - secondaryView.yMax - (superviewHeight - primaryView.y) - (2 * padding)
539 |
540 | case .aboveCentered:
541 | xOrigin = primaryView.xMid - (width / 2.0) + offset
542 | yOrigin = secondaryView.yMax + padding
543 | height = superviewHeight - secondaryView.yMax - (superviewHeight - primaryView.y) - (2 * padding)
544 |
545 | case .toTheLeftMatchingTop, .toTheLeftMatchingBottom, .toTheLeftCentered, .toTheRightMatchingTop, .toTheRightMatchingBottom, .toTheRightCentered:
546 | fatalError("[NEON] Invalid Align specified for alignBetweenVertical().")
547 | }
548 |
549 | if height < 0 {
550 | height = 0
551 | }
552 |
553 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
554 |
555 | if width == AutoWidth {
556 | self.setDimensionAutomatically()
557 | self.alignBetweenVertical(align: align, primaryView: primaryView, secondaryView: secondaryView, padding: padding, width: self.height, offset: offset)
558 | }
559 |
560 | }
561 | }
562 |
--------------------------------------------------------------------------------
/Source/NeonAnchorable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NeonAnchorable.swift
3 | // Neon
4 | //
5 | // Created by Mike on 10/1/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import UIKit
11 | #else
12 | import Cocoa
13 | #endif
14 |
15 |
16 | public protocol Anchorable : Frameable {}
17 |
18 | public extension Anchorable {
19 |
20 | /// Fill the superview, with optional padding values.
21 | ///
22 | /// - note: If you don't want padding, simply call `fillSuperview()` with no parameters.
23 | ///
24 | /// - parameters:
25 | /// - left: The padding between the left side of the view and the superview.
26 | ///
27 | /// - right: The padding between the right side of the view and the superview.
28 | ///
29 | /// - top: The padding between the top of the view and the superview.
30 | ///
31 | /// - bottom: The padding between the bottom of the view and the superview.
32 | ///
33 | public func fillSuperview(left: CGFloat = 0, right: CGFloat = 0, top: CGFloat = 0, bottom: CGFloat = 0) {
34 | let width : CGFloat = superFrame.width - (left + right)
35 | let height : CGFloat = superFrame.height - (top + bottom)
36 |
37 | frame = CGRect(x: left, y: top, width: width, height: height)
38 | }
39 |
40 |
41 | /// Anchor a view in the center of its superview.
42 | ///
43 | /// - parameters:
44 | /// - width: The width of the view.
45 | ///
46 | /// - height: The height of the view.
47 | ///
48 | public func anchorInCenter(width: CGFloat, height: CGFloat) {
49 | let xOrigin : CGFloat = (superFrame.width / 2.0) - (width / 2.0)
50 | let yOrigin : CGFloat = (superFrame.height / 2.0) - (height / 2.0)
51 |
52 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
53 |
54 | if height == AutoHeight {
55 | self.setDimensionAutomatically()
56 | self.anchorInCenter(width: width, height: self.height)
57 | }
58 |
59 | if width == AutoWidth {
60 | self.setDimensionAutomatically()
61 | self.anchorInCenter(width: self.width, height: height)
62 | }
63 | }
64 |
65 |
66 | /// Anchor a view in one of the four corners of its superview.
67 | ///
68 | /// - parameters:
69 | /// - corner: The `CornerType` value used to specify in which corner the view will be anchored.
70 | ///
71 | /// - xPad: The *horizontal* padding applied to the view inside its superview, which can be applied
72 | /// to the left or right side of the view, depending upon the `CornerType` specified.
73 | ///
74 | /// - yPad: The *vertical* padding applied to the view inside its supeview, which will either be on
75 | /// the top or bottom of the view, depending upon the `CornerType` specified.
76 | ///
77 | /// - width: The width of the view.
78 | ///
79 | /// - height: The height of the view.
80 | ///
81 | public func anchorInCorner(_ corner: Corner, xPad: CGFloat, yPad: CGFloat, width: CGFloat, height: CGFloat) {
82 | var xOrigin : CGFloat = 0.0
83 | var yOrigin : CGFloat = 0.0
84 |
85 | switch corner {
86 | case .topLeft:
87 | xOrigin = xPad
88 | yOrigin = yPad
89 |
90 | case .bottomLeft:
91 | xOrigin = xPad
92 | yOrigin = superFrame.height - height - yPad
93 |
94 | case .topRight:
95 | xOrigin = superFrame.width - width - xPad
96 | yOrigin = yPad
97 |
98 | case .bottomRight:
99 | xOrigin = superFrame.width - width - xPad
100 | yOrigin = superFrame.height - height - yPad
101 | }
102 |
103 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
104 |
105 | if height == AutoHeight {
106 | self.setDimensionAutomatically()
107 | self.anchorInCorner(corner, xPad: xPad, yPad: yPad, width: width, height: self.height)
108 | }
109 |
110 | if width == AutoWidth {
111 | self.setDimensionAutomatically()
112 | self.anchorInCorner(corner, xPad: xPad, yPad: yPad, width: self.width, height: height)
113 | }
114 |
115 | }
116 |
117 |
118 | /// Anchor a view in its superview, centered on a given edge.
119 | ///
120 | /// - parameters:
121 | /// - edge: The `Edge` used to specify which face of the superview the view
122 | /// will be anchored against and centered relative to.
123 | ///
124 | /// - padding: The padding applied to the view inside its superview. How this padding is applied
125 | /// will vary depending on the `Edge` provided. Views centered against the top or bottom of
126 | /// their superview will have the padding applied above or below them respectively, whereas views
127 | /// centered against the left or right side of their superview will have the padding applied to the
128 | /// right and left sides respectively.
129 | ///
130 | /// - width: The width of the view.
131 | ///
132 | /// - height: The height of the view.
133 | ///
134 | public func anchorToEdge(_ edge: Edge, padding: CGFloat, width: CGFloat, height: CGFloat) {
135 | var xOrigin : CGFloat = 0.0
136 | var yOrigin : CGFloat = 0.0
137 |
138 | switch edge {
139 | case .top:
140 | xOrigin = (superFrame.width / 2.0) - (width / 2.0)
141 | yOrigin = padding
142 |
143 | case .left:
144 | xOrigin = padding
145 | yOrigin = (superFrame.height / 2.0) - (height / 2.0)
146 |
147 | case .bottom:
148 | xOrigin = (superFrame.width / 2.0) - (width / 2.0)
149 | yOrigin = superFrame.height - height - padding
150 |
151 | case .right:
152 | xOrigin = superFrame.width - width - padding
153 | yOrigin = (superFrame.height / 2.0) - (height / 2.0)
154 | }
155 |
156 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
157 |
158 | if height == AutoHeight {
159 | self.setDimensionAutomatically()
160 | self.anchorToEdge(edge, padding: padding, width: width, height: self.height)
161 | }
162 | if width == AutoWidth {
163 | self.setDimensionAutomatically()
164 | self.anchorToEdge(edge, padding: padding, width: self.width, height: height)
165 | }
166 | }
167 |
168 |
169 | /// Anchor a view in its superview, centered on a given edge and filling either the width or
170 | /// height of that edge. For example, views anchored to the `.Top` or `.Bottom` will have
171 | /// their widths automatically sized to fill their superview, with the xPad applied to both
172 | /// the left and right sides of the view.
173 | ///
174 | /// - parameters:
175 | /// - edge: The `Edge` used to specify which face of the superview the view
176 | /// will be anchored against, centered relative to, and expanded to fill.
177 | ///
178 | /// - xPad: The horizontal padding applied to the view inside its superview. If the `Edge`
179 | /// specified is `.Top` or `.Bottom`, this padding will be applied to the left and right sides
180 | /// of the view when it fills the width superview.
181 | ///
182 | /// - yPad: The vertical padding applied to the view inside its superview. If the `Edge`
183 | /// specified is `.Left` or `.Right`, this padding will be applied to the top and bottom sides
184 | /// of the view when it fills the height of the superview.
185 | ///
186 | /// - otherSize: The size parameter that is *not automatically calculated* based on the provided
187 | /// edge. For example, views anchored to the `.Top` or `.Bottom` will have their widths automatically
188 | /// calculated, so `otherSize` will be applied to their height, and subsequently views anchored to
189 | /// the `.Left` and `.Right` will have `otherSize` applied to their width as their heights are
190 | /// automatically calculated.
191 | ///
192 | public func anchorAndFillEdge(_ edge: Edge, xPad: CGFloat, yPad: CGFloat, otherSize: CGFloat) {
193 | var xOrigin : CGFloat = 0.0
194 | var yOrigin : CGFloat = 0.0
195 | var width : CGFloat = 0.0
196 | var height : CGFloat = 0.0
197 | var autoSize : Bool = false
198 |
199 | switch edge {
200 | case .top:
201 | xOrigin = xPad
202 | yOrigin = yPad
203 | width = superFrame.width - (2 * xPad)
204 | height = otherSize
205 | autoSize = true
206 |
207 | case .left:
208 | xOrigin = xPad
209 | yOrigin = yPad
210 | width = otherSize
211 | height = superFrame.height - (2 * yPad)
212 |
213 | case .bottom:
214 | xOrigin = xPad
215 | yOrigin = superFrame.height - otherSize - yPad
216 | width = superFrame.width - (2 * xPad)
217 | height = otherSize
218 | autoSize = true
219 |
220 | case .right:
221 | xOrigin = superFrame.width - otherSize - xPad
222 | yOrigin = yPad
223 | width = otherSize
224 | height = superFrame.height - (2 * yPad)
225 | }
226 |
227 | frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
228 |
229 | if height == AutoHeight && autoSize {
230 | self.setDimensionAutomatically()
231 | self.anchorAndFillEdge(edge, xPad: xPad, yPad: yPad, otherSize: self.height)
232 | }
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/Source/NeonExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NeonExtensions.swift
3 | // Neon
4 | //
5 | // Created by Mike on 03/08/2016.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import UIKit
11 | typealias View = UIView
12 | #else
13 | import Cocoa
14 | typealias View = NSView
15 | #endif
16 |
17 | // MARK: UIView implementation of the Neon protocols.
18 | //
19 | extension View : Frameable, Anchorable, Alignable, Groupable {
20 | public var superFrame: CGRect {
21 | guard let superview = superview else {
22 | return CGRect.zero
23 | }
24 |
25 | return superview.frame
26 | }
27 |
28 | public func setDimensionAutomatically() {
29 | #if os(iOS)
30 | self.sizeToFit()
31 | #else
32 | self.autoresizesSubviews = true
33 | self.autoresizingMask = [.width, .height]
34 | #endif
35 | }
36 | }
37 |
38 |
39 | // MARK: CALayer implementation of the Neon protocols.
40 | //
41 | extension CALayer : Frameable, Anchorable, Alignable, Groupable {
42 | public var superFrame: CGRect {
43 | guard let superlayer = superlayer else {
44 | return CGRect.zero
45 | }
46 |
47 | return superlayer.frame
48 | }
49 |
50 | public func setDimensionAutomatically() { /* no-op here as this shouldn't apply to CALayers */ }
51 | }
52 |
--------------------------------------------------------------------------------
/Source/NeonFrameable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NeonFrameable.swift
3 | // Neon
4 | //
5 | // Created by Mike on 10/1/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import UIKit
11 | #else
12 | import Cocoa
13 | #endif
14 |
15 |
16 | /// Types adopting the `Frameable` protocol calculate specific `frame` information, as well as provide the
17 | /// frame information about their `superview` or `superlayer`.
18 | ///
19 | public protocol Frameable : class {
20 |
21 | var frame: CGRect { get set }
22 | var superFrame: CGRect { get }
23 |
24 | /// Get the x origin of a view.
25 | ///
26 | /// - returns: The minimum x value of the view's frame.
27 | ///
28 | /// Example
29 | /// -------
30 | /// let frame = CGRectMake(10.0, 20.0, 5.0, 7.0)
31 | /// frame.x() // returns 10.0
32 | ///
33 | var x: CGFloat { get }
34 |
35 |
36 | /// Get the mid x of a view.
37 | ///
38 | /// - returns: The middle x value of the view's frame
39 | ///
40 | /// Example
41 | /// -------
42 | /// let frame = CGRectMake(10.0, 20.0, 5.0, 7.0)
43 | /// frame.midX() // returns 7.5
44 | ///
45 | var xMid: CGFloat { get }
46 |
47 |
48 | /// Get the max x of a view.
49 | ///
50 | /// - returns: The maximum x value of the view's frame
51 | ///
52 | /// Example
53 | /// -------
54 | /// let frame = CGRectMake(10.0, 20.0, 5.0, 7.0)
55 | /// frame.maxX() // returns 15.0
56 | ///
57 | var xMax: CGFloat { get }
58 |
59 |
60 | /// Get the y origin of a view.
61 | ///
62 | /// - returns: The minimum y value of the view's frame.
63 | ///
64 | /// Example
65 | /// -------
66 | /// let frame = CGRectMake(10.0, 20.0, 5.0, 7.0)
67 | /// frame.y() // returns 20.0
68 | ///
69 | var y: CGFloat { get }
70 |
71 |
72 | /// Get the mid y of a view.
73 | ///
74 | /// - returns: The middle y value of the view's frame
75 | ///
76 | /// Example
77 | /// -------
78 | /// let frame = CGRectMake(10.0, 20.0, 5.0, 7.0)
79 | /// frame.midY() // returns 13.5
80 | ///
81 | var yMid: CGFloat { get }
82 |
83 |
84 | /// Get the max y of a view.
85 | ///
86 | /// - returns: The maximum y value of the view's frame.
87 | ///
88 | /// Example
89 | /// -------
90 | /// let frame = CGRectMake(10.0, 20.0, 5.0, 7.0)
91 | /// frame.maxY() // returns 27.0
92 | ///
93 | var yMax: CGFloat { get }
94 |
95 |
96 | /// Get the width of a view.
97 | ///
98 | /// - returns: The width of the view's frame.
99 | ///
100 | /// Example
101 | /// -------
102 | /// let frame = CGRectMake(10.0, 20.0, 5.0, 7.0)
103 | /// frame.width() // returns 5.0
104 | ///
105 | var width: CGFloat { get }
106 |
107 |
108 | /// Get the height of a view.
109 | ///
110 | /// - returns: The height of the view's frame.
111 | ///
112 | /// Example
113 | /// -------
114 | /// let frame = CGRectMake(10.0, 20.0, 5.0, 7.0)
115 | /// frame.height() // returns 7.0
116 | ///
117 | var height: CGFloat { get }
118 |
119 |
120 | /// *To be used internally* TODO: Determine how to make this either private or internal.
121 | ///
122 | ///
123 | func setDimensionAutomatically()
124 | }
125 |
126 |
127 | extension Frameable {
128 |
129 | public var x: CGFloat {
130 | return frame.minX
131 | }
132 |
133 | public var xMid: CGFloat {
134 | return frame.minX + (frame.width / 2.0)
135 | }
136 |
137 | public var xMax: CGFloat {
138 | return frame.maxX
139 | }
140 |
141 | public var y: CGFloat {
142 | return frame.minY
143 | }
144 |
145 | public var yMid: CGFloat {
146 | return frame.minY + (frame.height / 2.0)
147 | }
148 |
149 | public var yMax: CGFloat {
150 | return frame.maxY
151 | }
152 |
153 | public var width: CGFloat {
154 | return frame.width
155 | }
156 |
157 | public var height: CGFloat {
158 | return frame.height
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/Source/NeonGroupable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NeonGroupable.swift
3 | // Neon
4 | //
5 | // Created by Mike on 10/1/15.
6 | // Copyright © 2015 Mike Amaral. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import UIKit
11 | #else
12 | import Cocoa
13 | #endif
14 |
15 |
16 | public protocol Groupable : Frameable {}
17 |
18 | public extension Groupable {
19 |
20 | /// Tell a view to group an array of its subviews centered, specifying the padding between each subview,
21 | /// as well as the size of each.
22 | ///
23 | /// - parameters:
24 | /// - group: The `Group` type specifying if the subviews will be laid out horizontally or vertically in the center.
25 | ///
26 | /// - views: The array of views to grouped in the center. Depending on if the views are gouped horizontally
27 | /// or vertically, they will be positioned in order from left-to-right and top-to-bottom, respectively.
28 | ///
29 | /// - padding: The padding to be applied between the subviews.
30 | ///
31 | /// - width: The width of each subview.
32 | ///
33 | /// - height: The height of each subview.
34 | ///
35 | public func groupInCenter(group: Group, views: [Frameable], padding: CGFloat, width: CGFloat, height: CGFloat) {
36 | if views.count == 0 {
37 | print("[NEON] Warning: No subviews provided to groupInCenter().")
38 | return
39 | }
40 |
41 | var xOrigin : CGFloat = 0.0
42 | var yOrigin : CGFloat = 0.0
43 | var xAdjust : CGFloat = 0.0
44 | var yAdjust : CGFloat = 0.0
45 |
46 | switch group {
47 | case .horizontal:
48 | xOrigin = (self.width - (CGFloat(views.count) * width) - (CGFloat(views.count - 1) * padding)) / 2.0
49 | yOrigin = (self.height / 2.0) - (height / 2.0)
50 | xAdjust = width + padding
51 |
52 | case .vertical:
53 | xOrigin = (self.width / 2.0) - (width / 2.0)
54 | yOrigin = (self.height - (CGFloat(views.count) * height) - (CGFloat(views.count - 1) * padding)) / 2.0
55 | yAdjust = height + padding
56 | }
57 |
58 | for view in views {
59 | view.frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
60 |
61 | xOrigin += xAdjust
62 | yOrigin += yAdjust
63 | }
64 | }
65 |
66 |
67 | /// Tell a view to group an array of its subviews in one of its corners, specifying the padding between each subview,
68 | /// as well as the size of each.
69 | ///
70 | /// - parameters:
71 | /// - group: The `Group` type specifying if the subviews will be laid out horizontally or vertically in the corner.
72 | ///
73 | /// - views: The array of views to grouped in the specified corner. Depending on if the views are gouped horizontally
74 | /// or vertically, they will be positioned in order from left-to-right and top-to-bottom, respectively.
75 | ///
76 | /// - inCorner: The specified corner the views will be grouped in.
77 | ///
78 | /// - padding: The padding to be applied between the subviews and their superview.
79 | ///
80 | /// - width: The width of each subview.
81 | ///
82 | /// - height: The height of each subview.
83 | ///
84 | public func groupInCorner(group: Group, views: [Frameable], inCorner corner: Corner, padding: CGFloat, width: CGFloat, height: CGFloat) {
85 | switch group {
86 | case .horizontal:
87 | groupInCornerHorizontal(views: views, inCorner: corner, padding: padding, width: width, height: height)
88 |
89 | case .vertical:
90 | groupInCornerVertical(views: views, inCorner: corner, padding: padding, width: width, height: height)
91 | }
92 | }
93 |
94 |
95 | /// Tell a view to group an array of its subviews against one of its edges, specifying the padding between each subview
96 | /// and their superview, as well as the size of each.
97 | ///
98 | /// - parameters:
99 | /// - group: The `Group` type specifying if the subviews will be laid out horizontally or vertically against the specified
100 | /// edge.
101 | ///
102 | /// - views: The array of views to grouped against the spcified edge. Depending on if the views are gouped horizontally
103 | /// or vertically, they will be positioned in-order from left-to-right and top-to-bottom, respectively.
104 | ///
105 | /// - againstEdge: The specified edge the views will be grouped against.
106 | ///
107 | /// - padding: The padding to be applied between each of the subviews and their superview.
108 | ///
109 | /// - width: The width of each subview.
110 | ///
111 | /// - height: The height of each subview.
112 | ///
113 | public func groupAgainstEdge(group: Group, views: [Frameable], againstEdge edge: Edge, padding: CGFloat, width: CGFloat, height: CGFloat) {
114 | if views.count == 0 {
115 | print("[NEON] Warning: No subviews provided to groupAgainstEdge().")
116 | return
117 | }
118 |
119 | var xOrigin : CGFloat = 0.0
120 | var yOrigin : CGFloat = 0.0
121 | var xAdjust : CGFloat = 0.0
122 | var yAdjust : CGFloat = 0.0
123 |
124 | switch edge {
125 | case .top:
126 | if group == .horizontal {
127 | xOrigin = (self.width - (CGFloat(views.count) * width) - (CGFloat(views.count - 1) * padding)) / 2.0
128 | xAdjust = width + padding
129 | } else {
130 | xOrigin = (self.width / 2.0) - (width / 2.0)
131 | yAdjust = height + padding
132 | }
133 |
134 | yOrigin = padding
135 |
136 | case .left:
137 | if group == .horizontal {
138 | yOrigin = (self.height / 2.0) - (height / 2.0)
139 | xAdjust = width + padding
140 | } else {
141 | yOrigin = (self.height - (CGFloat(views.count) * height) - (CGFloat(views.count - 1) * padding)) / 2.0
142 | yAdjust = height + padding
143 | }
144 |
145 | xOrigin = padding
146 |
147 | case .bottom:
148 | if group == .horizontal {
149 | xOrigin = (self.width - (CGFloat(views.count) * width) - (CGFloat(views.count - 1) * padding)) / 2.0
150 | yOrigin = self.height - height - padding
151 | xAdjust = width + padding
152 | } else {
153 | xOrigin = (self.width / 2.0) - (width / 2.0)
154 | yOrigin = self.height - (CGFloat(views.count) * height) - (CGFloat(views.count) * padding)
155 | yAdjust = height + padding
156 | }
157 |
158 | case .right:
159 | if group == .horizontal {
160 | xOrigin = self.width - (CGFloat(views.count) * width) - (CGFloat(views.count) * padding)
161 | yOrigin = (self.height / 2.0) - (height / 2.0)
162 | xAdjust = width + padding
163 | } else {
164 | xOrigin = self.width - width - padding
165 | yOrigin = (self.height - (CGFloat(views.count) * height) - (CGFloat(views.count - 1) * padding)) / 2.0
166 | yAdjust = height + padding
167 | }
168 | }
169 |
170 | for view in views {
171 | view.frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
172 |
173 | xOrigin += xAdjust
174 | yOrigin += yAdjust
175 | }
176 | }
177 |
178 |
179 | /// Tell a view to group an array of its subviews relative to another of that view's subview, specifying the padding between
180 | /// each.
181 | ///
182 | /// - parameters:
183 | /// - group: The `Group` type specifying if the subviews will be laid out horizontally or vertically against the specified
184 | /// sibling.
185 | ///
186 | /// - andAlign: the `Align` type specifying how the views will be aligned relative to the sibling.
187 | ///
188 | /// - views: The array of views to grouped against the sibling. Depending on if the views are gouped horizontally
189 | /// or vertically, they will be positioned in-order from left-to-right and top-to-bottom, respectively.
190 | ///
191 | /// - relativeTo: The sibling view that the views will be aligned relative to.
192 | ///
193 | /// - padding: The padding to be applied between each of the subviews and the sibling.
194 | ///
195 | /// - width: The width of each subview.
196 | ///
197 | /// - height: The height of each subview.
198 | ///
199 | public func groupAndAlign(group: Group, andAlign align: Align, views: [Frameable], relativeTo sibling: Frameable, padding: CGFloat, width: CGFloat, height: CGFloat) {
200 | switch group {
201 | case .horizontal:
202 | groupAndAlignHorizontal(align: align, views: views, relativeTo: sibling, padding: padding, width: width, height: height)
203 |
204 | case .vertical:
205 | groupAndAlignVertical(align: align, views: views, relativeTo: sibling, padding: padding, width: width, height: height)
206 | }
207 | }
208 |
209 |
210 | /// Tell a view to group an array of its subviews filling the width and height of the superview, specifying the padding between
211 | /// each subview and the superview.
212 | ///
213 | /// - parameters:
214 | /// - group: The `Group` type specifying if the subviews will be laid out horizontally or vertically.
215 | ///
216 | /// - views: The array of views to be grouped against the sibling. Depending on if the views are grouped horizontally
217 | /// or vertically, they will be positions in-order from left-to-right and top-to-bottom, respectively.
218 | ///
219 | /// - padding: The padding to be applied between each of the subviews and the sibling.
220 | ///
221 | public func groupAndFill(group: Group, views: [Frameable], padding: CGFloat) {
222 | if views.count == 0 {
223 | print("[NEON] Warning: No subviews provided to groupAndFill().")
224 | return
225 | }
226 |
227 | var xOrigin : CGFloat = padding
228 | var yOrigin : CGFloat = padding
229 | var width : CGFloat = 0.0
230 | var height : CGFloat = 0.0
231 | var xAdjust : CGFloat = 0.0
232 | var yAdjust : CGFloat = 0.0
233 |
234 | switch group {
235 | case .horizontal:
236 | width = (self.width - (CGFloat(views.count + 1) * padding)) / CGFloat(views.count)
237 | height = self.height - (2 * padding)
238 | xAdjust = width + padding
239 |
240 | case .vertical:
241 | width = self.width - (2 * padding)
242 | height = (self.height - (CGFloat(views.count + 1) * padding)) / CGFloat(views.count)
243 | yAdjust = height + padding
244 | }
245 |
246 | for view in views {
247 | view.frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
248 |
249 | xOrigin += xAdjust
250 | yOrigin += yAdjust
251 | }
252 | }
253 |
254 |
255 |
256 | // MARK: Private utils
257 | //
258 | fileprivate func groupInCornerHorizontal(views: [Frameable], inCorner corner: Corner, padding: CGFloat, width: CGFloat, height: CGFloat) {
259 | if views.count == 0 {
260 | print("[NEON] Warning: No subviews provided to groupInCorner().")
261 | return
262 | }
263 |
264 | var xOrigin : CGFloat = 0.0
265 | var yOrigin : CGFloat = 0.0
266 | let xAdjust : CGFloat = width + padding
267 |
268 | switch corner {
269 | case .topLeft:
270 | xOrigin = padding
271 | yOrigin = padding
272 |
273 | case .topRight:
274 | xOrigin = self.width - ((CGFloat(views.count) * width) + (CGFloat(views.count) * padding))
275 | yOrigin = padding
276 |
277 | case .bottomLeft:
278 | xOrigin = padding
279 | yOrigin = self.height - height - padding
280 |
281 | case .bottomRight:
282 | xOrigin = self.width - ((CGFloat(views.count) * width) + (CGFloat(views.count) * padding))
283 | yOrigin = self.height - height - padding
284 | }
285 |
286 | for view in views {
287 | view.frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
288 |
289 | xOrigin += xAdjust
290 | }
291 | }
292 |
293 | fileprivate func groupInCornerVertical(views: [Frameable], inCorner corner: Corner, padding: CGFloat, width: CGFloat, height: CGFloat) {
294 | if views.count == 0 {
295 | print("[NEON] Warning: No subviews provided to groupInCorner().")
296 | return
297 | }
298 |
299 | var xOrigin : CGFloat = 0.0
300 | var yOrigin : CGFloat = 0.0
301 | let yAdjust : CGFloat = height + padding
302 |
303 | switch corner {
304 | case .topLeft:
305 | xOrigin = padding
306 | yOrigin = padding
307 |
308 | case .topRight:
309 | xOrigin = self.width - width - padding
310 | yOrigin = padding
311 |
312 | case .bottomLeft:
313 | xOrigin = padding
314 | yOrigin = self.height - ((CGFloat(views.count) * height) + (CGFloat(views.count) * padding))
315 |
316 | case .bottomRight:
317 | xOrigin = self.width - width - padding
318 | yOrigin = self.height - ((CGFloat(views.count) * height) + (CGFloat(views.count) * padding))
319 | }
320 |
321 | for view in views {
322 | view.frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
323 |
324 | yOrigin += yAdjust
325 | }
326 | }
327 |
328 | fileprivate func groupAndAlignHorizontal(align: Align, views: [Frameable], relativeTo sibling: Frameable, padding: CGFloat, width: CGFloat, height: CGFloat) {
329 | if views.count == 0 {
330 | print("[NEON] Warning: No subviews provided to groupAndAlign().")
331 | return
332 | }
333 |
334 | var xOrigin : CGFloat = 0.0
335 | var yOrigin : CGFloat = 0.0
336 | let xAdjust : CGFloat = width + padding
337 |
338 | switch align {
339 | case .toTheRightMatchingTop:
340 | xOrigin = sibling.xMax + padding
341 | yOrigin = sibling.y
342 |
343 | case .toTheRightMatchingBottom:
344 | xOrigin = sibling.xMax + padding
345 | yOrigin = sibling.yMax - height
346 |
347 | case .toTheRightCentered:
348 | xOrigin = sibling.xMax + padding
349 | yOrigin = sibling.yMid - (height / 2.0)
350 |
351 | case .toTheLeftMatchingTop:
352 | xOrigin = sibling.x - (CGFloat(views.count) * width) - (CGFloat(views.count) * padding)
353 | yOrigin = sibling.y
354 |
355 | case .toTheLeftMatchingBottom:
356 | xOrigin = sibling.x - (CGFloat(views.count) * width) - (CGFloat(views.count) * padding)
357 | yOrigin = sibling.yMax - height
358 |
359 | case .toTheLeftCentered:
360 | xOrigin = sibling.x - (CGFloat(views.count) * width) - (CGFloat(views.count) * padding)
361 | yOrigin = sibling.yMid - (height / 2.0)
362 |
363 | case .underMatchingLeft:
364 | xOrigin = sibling.x
365 | yOrigin = sibling.yMax + padding
366 |
367 | case .underMatchingRight:
368 | xOrigin = sibling.xMax - (CGFloat(views.count) * width) - (CGFloat(views.count - 1) * padding)
369 | yOrigin = sibling.yMax + padding
370 |
371 | case .underCentered:
372 | xOrigin = sibling.xMid - ((CGFloat(views.count) * width) + (CGFloat(views.count - 1) * padding)) / 2.0
373 | yOrigin = sibling.yMax + padding
374 |
375 | case .aboveMatchingLeft:
376 | xOrigin = sibling.x
377 | yOrigin = sibling.y - height - padding
378 |
379 | case .aboveMatchingRight:
380 | xOrigin = sibling.xMax - (CGFloat(views.count) * width) - (CGFloat(views.count - 1) * padding)
381 | yOrigin = sibling.y - height - padding
382 |
383 | case .aboveCentered:
384 | xOrigin = sibling.xMid - ((CGFloat(views.count) * width) + (CGFloat(views.count - 1) * padding)) / 2.0
385 | yOrigin = sibling.y - height - padding
386 | }
387 |
388 | for view in views {
389 | view.frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
390 |
391 | xOrigin += xAdjust
392 | }
393 | }
394 |
395 | fileprivate func groupAndAlignVertical(align: Align, views: [Frameable], relativeTo sibling: Frameable, padding: CGFloat, width: CGFloat, height: CGFloat) {
396 | if views.count == 0 {
397 | print("[NEON] Warning: No subviews provided to groupAndAlign().")
398 | return
399 | }
400 |
401 | var xOrigin : CGFloat = 0.0
402 | var yOrigin : CGFloat = 0.0
403 | let yAdjust : CGFloat = height + padding
404 |
405 | switch align {
406 | case .toTheRightMatchingTop:
407 | xOrigin = sibling.xMax + padding
408 | yOrigin = sibling.y
409 |
410 | case .toTheRightMatchingBottom:
411 | xOrigin = sibling.xMax + padding
412 | yOrigin = sibling.yMax - (CGFloat(views.count) * height) - (CGFloat(views.count - 1) * padding)
413 |
414 | case .toTheRightCentered:
415 | xOrigin = sibling.xMax + padding
416 | yOrigin = sibling.yMid - ((CGFloat(views.count) * height) + CGFloat(views.count - 1) * padding) / 2.0
417 |
418 | case .toTheLeftMatchingTop:
419 | xOrigin = sibling.x - width - padding
420 | yOrigin = sibling.y
421 |
422 | case .toTheLeftMatchingBottom:
423 | xOrigin = sibling.x - width - padding
424 | yOrigin = sibling.yMax - (CGFloat(views.count) * height) - (CGFloat(views.count - 1) * padding)
425 |
426 | case .toTheLeftCentered:
427 | xOrigin = sibling.x - width - padding
428 | yOrigin = sibling.yMid - ((CGFloat(views.count) * height) + CGFloat(views.count - 1) * padding) / 2.0
429 |
430 | case .underMatchingLeft:
431 | xOrigin = sibling.x
432 | yOrigin = sibling.yMax + padding
433 |
434 | case .underMatchingRight:
435 | xOrigin = sibling.xMax - width
436 | yOrigin = sibling.yMax + padding
437 |
438 | case .underCentered:
439 | xOrigin = sibling.xMid - (width / 2.0)
440 | yOrigin = sibling.yMax + padding
441 |
442 | case .aboveMatchingLeft:
443 | xOrigin = sibling.x
444 | yOrigin = sibling.y - (CGFloat(views.count) * height) - (CGFloat(views.count) * padding)
445 |
446 | case .aboveMatchingRight:
447 | xOrigin = sibling.xMax - width
448 | yOrigin = sibling.y - (CGFloat(views.count) * height) - (CGFloat(views.count) * padding)
449 |
450 | case .aboveCentered:
451 | xOrigin = sibling.xMid - (width / 2.0)
452 | yOrigin = sibling.y - (CGFloat(views.count) * height) - (CGFloat(views.count) * padding)
453 | }
454 |
455 | for view in views {
456 | view.frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)
457 |
458 | yOrigin += yAdjust
459 | }
460 | }
461 | }
462 |
--------------------------------------------------------------------------------