├── .gitignore ├── LICENSE ├── airbnb-datepicker ├── airbnb-datepicker.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── airbnb-datepicker │ ├── AirbnbDatePicker.swift │ ├── AirbnbDatePickerCell.swift │ ├── AirbnbDatePickerFlowLayout.swift │ ├── AirbnbDatePickerFooter.swift │ ├── AirbnbDatePickerHeader.swift │ ├── AirbnbDatePickerMonthHeader.swift │ ├── AirbnbDatePickerViewController.swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Calendar.imageset │ │ ├── Calendar-100.png │ │ └── Contents.json │ ├── Contents.json │ └── Delete.imageset │ │ ├── Contents.json │ │ └── Delete-75 (1).png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── BaseCell.swift │ ├── Date.swift │ ├── Info.plist │ ├── Theme.swift │ ├── UIColor.swift │ ├── Utility.swift │ └── ViewController.swift ├── airbnb-main ├── airbnb-main.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── airbnb-main │ ├── AirbnbButton.swift │ ├── AirbnbCategoryTableCell.swift │ ├── AirbnbContentCategory.swift │ ├── AirbnbContentThumbnail.swift │ ├── AirbnbExploreController.swift │ ├── AirbnbExploreFeedController.swift │ ├── AirbnbExploreHeaderView.swift │ ├── AirbnbExploreHomeController.swift │ ├── AirbnbHome.swift │ ├── AirbnbHomeDetailController.swift │ ├── AirbnbHomeItemCell.swift │ ├── AirbnbHomeItemTableCell.swift │ ├── AirbnbIconLabel.swift │ ├── AirbnbMainController.swift │ ├── AirbnbMapController.swift │ ├── AirbnbMapItemCell.swift │ ├── AirbnbPageTabItem.swift │ ├── AirbnbPageTabNavigationView.swift │ ├── AirbnbReview.swift │ ├── AirbnbStar.swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Back.imageset │ │ ├── Back-25.png │ │ ├── Back-50.png │ │ ├── Back-75.png │ │ └── Contents.json │ ├── Bed.imageset │ │ ├── Bed-25.png │ │ ├── Bed-50 (1).png │ │ ├── Bed-75.png │ │ └── Contents.json │ ├── Collapse.imageset │ │ ├── Collapse Arrow-25 (1).png │ │ ├── Collapse Arrow-50 (1).png │ │ ├── Collapse Arrow-75 (1).png │ │ └── Contents.json │ ├── Contents.json │ ├── Door.imageset │ │ ├── Contents.json │ │ ├── Door Opened-25.png │ │ ├── Door Opened-50.png │ │ └── Door Opened-75.png │ ├── Expand.imageset │ │ ├── Contents.json │ │ ├── Expand Arrow-25.png │ │ ├── Expand Arrow-50 (1).png │ │ └── Expand Arrow-75.png │ ├── Globe.imageset │ │ ├── Contents.json │ │ ├── Globe Earth-25.png │ │ ├── Globe Earth-50.png │ │ └── Globe Earth-75.png │ ├── Heart.imageset │ │ ├── Contents.json │ │ ├── Hearts-25.png │ │ ├── Hearts-50 (1).png │ │ └── Hearts-75.png │ ├── Message.imageset │ │ ├── Contents.json │ │ ├── Message-25.png │ │ ├── Message-50.png │ │ └── Message-75.png │ ├── Profile.imageset │ │ ├── Contents.json │ │ ├── User-25.png │ │ ├── User-50.png │ │ └── User-75.png │ ├── Search.imageset │ │ ├── Contents.json │ │ ├── Search-25.png │ │ ├── Search-50.png │ │ └── Search-75.png │ ├── Shower.imageset │ │ ├── Contents.json │ │ ├── Shower and Tub-25.png │ │ ├── Shower and Tub-50.png │ │ └── Shower and Tub-75.png │ ├── Star Empty.imageset │ │ ├── Contents.json │ │ ├── Star-25.png │ │ ├── Star-50.png │ │ └── Star-75.png │ ├── Star Filled.imageset │ │ ├── Contents.json │ │ ├── Star Filled-25.png │ │ ├── Star Filled-50.png │ │ └── Star Filled-75.png │ ├── Star Half Empty.imageset │ │ ├── Contents.json │ │ ├── Star Half Empty-25.png │ │ ├── Star Half Empty-50.png │ │ └── Star Half Empty-75.png │ ├── Upload.imageset │ │ ├── Contents.json │ │ ├── Upload-25.png │ │ ├── Upload-50.png │ │ └── Upload-75.png │ ├── home-1.imageset │ │ ├── Contents.json │ │ └── home-1.jpeg │ ├── home-10.imageset │ │ ├── Contents.json │ │ └── home-10.jpeg │ ├── home-2.imageset │ │ ├── Contents.json │ │ └── home-2.jpeg │ ├── home-3.imageset │ │ ├── Contents.json │ │ └── home-3.jpeg │ ├── home-4.imageset │ │ ├── Contents.json │ │ └── home-4.jpeg │ ├── home-5.imageset │ │ ├── Contents.json │ │ └── home-5.jpeg │ ├── home-6.imageset │ │ ├── Contents.json │ │ └── home-6.jpeg │ ├── home-7.imageset │ │ ├── Contents.json │ │ └── home-7.jpeg │ ├── home-8.imageset │ │ ├── Contents.json │ │ └── home-8.jpeg │ ├── home-9.imageset │ │ ├── Contents.json │ │ └── home-9.jpeg │ └── profpic.imageset │ │ ├── Contents.json │ │ └── profpic.jpeg │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── BaseCollectionCell.swift │ ├── BasePageTabItemView.swift │ ├── BaseTableCell.swift │ ├── BaseTableController.swift │ ├── ImageExpandAnimationController.swift │ ├── ImageShrinkAnimationController.swift │ ├── Info.plist │ ├── String.swift │ ├── Theme.swift │ ├── UIColor.swift │ ├── UIImage.swift │ ├── UITabBar.swift │ └── VerticalAlignLabel.swift ├── airbnb-occupant-filter ├── airbnb-occupant-filter.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── airbnb-occupant-filter │ ├── AirbnbCounter.swift │ ├── AirbnbOccupantFilter.swift │ ├── AirbnbOccupantFilterController.swift │ ├── AirbnbSwitch.swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Checkmark.imageset │ │ ├── Checkmark-25.png │ │ ├── Checkmark-50.png │ │ ├── Checkmark-75.png │ │ └── Contents.json │ ├── Contents.json │ ├── Delete.imageset │ │ ├── Contents.json │ │ ├── Delete-25.png │ │ ├── Delete-50.png │ │ └── Delete-75.png │ ├── Family.imageset │ │ ├── Contents.json │ │ ├── Family Man Woman-25.png │ │ ├── Family Man Woman-50.png │ │ └── Family Man Woman-75.png │ ├── Minus.imageset │ │ ├── Contents.json │ │ ├── Minus-25.png │ │ ├── Minus-48.png │ │ └── Minus-75.png │ └── Plus.imageset │ │ ├── Contents.json │ │ ├── Plus Math-25.png │ │ ├── Plus Math-48.png │ │ └── Plus Math-75.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── Theme.swift │ ├── UIColor.swift │ ├── UIImage.swift │ └── ViewController.swift ├── frameworks ├── airbnb-datepicker │ ├── airbnb-datepicker.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ └── airbnb-datepicker │ │ ├── AirbnbDatePicker.swift │ │ ├── AirbnbDatePickerCell.swift │ │ ├── AirbnbDatePickerFlowLayout.swift │ │ ├── AirbnbDatePickerFooter.swift │ │ ├── AirbnbDatePickerHeader.swift │ │ ├── AirbnbDatePickerMonthHeader.swift │ │ ├── AirbnbDatePickerViewController.swift │ │ ├── Assets.xcassets │ │ ├── Calendar.imageset │ │ │ ├── Calendar-25.png │ │ │ ├── Calendar-50.png │ │ │ ├── Calendar-75.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Delete.imageset │ │ │ ├── Contents.json │ │ │ └── Delete-75 (1).png │ │ ├── BaseCell.swift │ │ ├── Date.swift │ │ ├── Info.plist │ │ ├── Theme.swift │ │ ├── UIColor.swift │ │ ├── Utility.swift │ │ └── airbnb-datepicker.h └── airbnb-occupant-filter │ ├── airbnb-occupant-filter.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── airbnb-occupant-filter │ ├── AirbnbCounter.swift │ ├── AirbnbOccupantFilter.swift │ ├── AirbnbOccupantFilterController.swift │ ├── AirbnbSwitch.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Checkmark.imageset │ │ ├── Checkmark-25.png │ │ ├── Checkmark-50.png │ │ ├── Checkmark-75.png │ │ └── Contents.json │ ├── Contents.json │ ├── Delete.imageset │ │ ├── Contents.json │ │ ├── Delete-25.png │ │ ├── Delete-50.png │ │ └── Delete-75.png │ ├── Family.imageset │ │ ├── Contents.json │ │ ├── Family Man Woman-25.png │ │ ├── Family Man Woman-50.png │ │ └── Family Man Woman-75.png │ ├── Minus.imageset │ │ ├── Contents.json │ │ ├── Minus-25.png │ │ ├── Minus-48.png │ │ └── Minus-75.png │ └── Plus.imageset │ │ ├── Contents.json │ │ ├── Plus Math-25.png │ │ ├── Plus Math-48.png │ │ └── Plus Math-75.png │ ├── Info.plist │ ├── Theme.swift │ ├── UIColor.swift │ ├── UIImage.swift │ └── airbnb-occupant-filter.h └── readme.md /.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 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 yonasstephen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/AirbnbDatePicker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePicker.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 22/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class AirbnbDatePicker: UIView, AirbnbDatePickerDelegate { 12 | 13 | public var selectedStartDate: Date? 14 | public var selectedEndDate: Date? 15 | public var delegate: UIViewController? 16 | 17 | var dateFormatter: DateFormatter { 18 | get { 19 | let f = DateFormatter() 20 | f.dateFormat = "d MMM" 21 | return f 22 | } 23 | } 24 | 25 | public override init(frame: CGRect) { 26 | super.init(frame: frame) 27 | setupViews() 28 | } 29 | 30 | public required init?(coder aDecoder: NSCoder) { 31 | fatalError("init(coder:) has not been implemented") 32 | } 33 | 34 | public lazy var dateInputButton: UIButton = { 35 | let btn = UIButton() 36 | btn.translatesAutoresizingMaskIntoConstraints = false 37 | btn.backgroundColor = Theme.PRIMARY_COLOR 38 | btn.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left 39 | btn.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) 40 | btn.adjustsImageWhenHighlighted = false 41 | btn.addTarget(self, action: #selector(AirbnbDatePicker.showDatePicker), for: .touchUpInside) 42 | 43 | let img = UIImage(named: "Calendar", in: Bundle(for: AirbnbDatePicker.self), compatibleWith: nil) 44 | btn.setImage(img, for: .normal) 45 | btn.imageView?.contentMode = .scaleAspectFit 46 | 47 | btn.setTitle("Anytime", for: .normal) 48 | btn.setTitleColor(UIColor.white, for: .normal) 49 | btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 15) 50 | btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0) 51 | btn.titleLabel?.lineBreakMode = .byTruncatingTail 52 | return btn 53 | }() 54 | 55 | func setupViews() { 56 | addSubview(dateInputButton) 57 | 58 | dateInputButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 59 | dateInputButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 60 | dateInputButton.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 61 | dateInputButton.heightAnchor.constraint(equalTo: heightAnchor).isActive = true 62 | } 63 | 64 | @objc func showDatePicker() { 65 | let datePickerViewController = AirbnbDatePickerViewController(dateFrom: selectedStartDate, dateTo: selectedEndDate) 66 | datePickerViewController.delegate = self 67 | let navigationController = UINavigationController(rootViewController: datePickerViewController) 68 | delegate?.present(navigationController, animated: true, completion: nil) 69 | } 70 | 71 | public func datePickerController(_ datePickerController: AirbnbDatePickerViewController, didSaveStartDate startDate: Date?, endDate: Date?) { 72 | selectedStartDate = startDate 73 | selectedEndDate = endDate 74 | 75 | if selectedStartDate == nil && selectedEndDate == nil { 76 | dateInputButton.setTitle("Anytime", for: .normal) 77 | 78 | } else { 79 | dateInputButton.setTitle("\(dateFormatter.string(from: startDate!)) - \(dateFormatter.string(from: endDate!))", for: .normal) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/AirbnbDatePickerCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePickerCell.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 23/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbDatePickerCell: BaseCell { 12 | 13 | var type: AirbnbDatePickerCellType! = [] 14 | 15 | var dateLabel: UILabel = { 16 | let label = UILabel() 17 | label.translatesAutoresizingMaskIntoConstraints = false 18 | label.textAlignment = .center 19 | label.font = UIFont.boldSystemFont(ofSize: 15) 20 | label.textColor = UIColor.white 21 | return label 22 | }() 23 | 24 | var highlightView: UIView = { 25 | let view = UIView() 26 | view.translatesAutoresizingMaskIntoConstraints = false 27 | view.backgroundColor = UIColor.clear 28 | return view 29 | }() 30 | 31 | override func setupViews() { 32 | super.setupViews() 33 | 34 | addSubview(dateLabel) 35 | 36 | dateLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 37 | dateLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 38 | dateLabel.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 39 | dateLabel.heightAnchor.constraint(equalTo: heightAnchor).isActive = true 40 | 41 | addSubview(highlightView) 42 | 43 | highlightView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 44 | highlightView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 45 | highlightView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 46 | highlightView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true 47 | } 48 | 49 | func configureCell() { 50 | if type.contains(.Selected) || type.contains(.SelectedStartDate) || type.contains(.SelectedEndDate) || type.contains(.InBetweenDate) { 51 | 52 | dateLabel.layer.cornerRadius = 0 53 | dateLabel.layer.borderColor = UIColor.white.cgColor 54 | dateLabel.layer.borderWidth = 1 55 | dateLabel.layer.backgroundColor = UIColor.white.cgColor 56 | dateLabel.layer.mask = nil 57 | dateLabel.textColor = Theme.SECONDARY_COLOR 58 | 59 | if type.contains(.SelectedStartDate) { 60 | let side = frame.size.width / 2 61 | let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .bottomLeft], cornerRadii: CGSize(width: side, height: side)) 62 | let shape = CAShapeLayer() 63 | shape.path = maskPath.cgPath 64 | dateLabel.layer.mask = shape 65 | 66 | } else if type.contains(.SelectedEndDate) { 67 | let side = frame.size.width / 2 68 | let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topRight, .bottomRight], cornerRadii: CGSize(width: side, height: side)) 69 | let shape = CAShapeLayer() 70 | shape.path = maskPath.cgPath 71 | dateLabel.layer.mask = shape 72 | 73 | } else if !type.contains(.InBetweenDate) { 74 | dateLabel.layer.cornerRadius = frame.size.width / 2 75 | } 76 | 77 | } else if type.contains(.PastDate) { 78 | 79 | dateLabel.layer.cornerRadius = 0 80 | dateLabel.layer.borderColor = UIColor.clear.cgColor 81 | dateLabel.layer.borderWidth = 0 82 | dateLabel.layer.backgroundColor = UIColor.clear.cgColor 83 | dateLabel.layer.mask = nil 84 | dateLabel.textColor = Theme.SECONDARY_COLOR 85 | 86 | } else if type.contains(.Today) { 87 | 88 | dateLabel.layer.cornerRadius = self.frame.size.width / 2 89 | dateLabel.layer.borderColor = Theme.SECONDARY_COLOR.cgColor 90 | dateLabel.layer.borderWidth = 1 91 | dateLabel.layer.backgroundColor = UIColor.clear.cgColor 92 | dateLabel.layer.mask = nil 93 | dateLabel.textColor = UIColor.white 94 | 95 | } else { 96 | 97 | dateLabel.layer.cornerRadius = 0 98 | dateLabel.layer.borderColor = UIColor.clear.cgColor 99 | dateLabel.layer.borderWidth = 0 100 | dateLabel.layer.backgroundColor = UIColor.clear.cgColor 101 | dateLabel.layer.mask = nil 102 | dateLabel.textColor = UIColor.white 103 | 104 | } 105 | 106 | 107 | if type.contains(.Highlighted) { 108 | highlightView.backgroundColor = UIColor(red: 200 / 255, green: 200 / 255, blue: 200 / 255, alpha: 0.6) 109 | highlightView.layer.cornerRadius = frame.size.width / 2 110 | } else { 111 | highlightView.backgroundColor = UIColor.clear 112 | } 113 | } 114 | } 115 | 116 | struct AirbnbDatePickerCellType: OptionSet { 117 | let rawValue: Int 118 | 119 | static let Date = AirbnbDatePickerCellType(rawValue: 1 << 0) // has number 120 | static let Empty = AirbnbDatePickerCellType(rawValue: 1 << 1) // has no number 121 | static let PastDate = AirbnbDatePickerCellType(rawValue: 1 << 2) // disabled 122 | static let Today = AirbnbDatePickerCellType(rawValue: 1 << 3) // has circle 123 | static let Selected = AirbnbDatePickerCellType(rawValue: 1 << 4) // has filled circle 124 | static let SelectedStartDate = AirbnbDatePickerCellType(rawValue: 1 << 5) // has half filled circle on the left 125 | static let SelectedEndDate = AirbnbDatePickerCellType(rawValue: 1 << 6) // has half filled circle on the right 126 | static let InBetweenDate = AirbnbDatePickerCellType(rawValue: 1 << 7) // has filled square 127 | static let Highlighted = AirbnbDatePickerCellType(rawValue: 1 << 8) // has 128 | } 129 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/AirbnbDatePickerFlowLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePickerFlowLayout.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 28/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbDatePickerFlowLayout : UICollectionViewFlowLayout { 12 | 13 | let cellSpacing:CGFloat = 0 14 | 15 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 16 | if let attributes = super.layoutAttributesForElements(in: rect) { 17 | for (index, attribute) in attributes.enumerated() { 18 | if index == 0 { continue } 19 | let prevLayoutAttributes = attributes[index - 1] 20 | let origin = prevLayoutAttributes.frame.maxX 21 | if origin + cellSpacing + attribute.frame.size.width < self.collectionViewContentSize.width { 22 | attribute.frame.origin.x = origin + cellSpacing 23 | } 24 | } 25 | return attributes 26 | } 27 | return nil 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/AirbnbDatePickerFooter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePickerFooter.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 24/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol AirbnbDatePickerFooterDelegate { 12 | func didSave() 13 | } 14 | 15 | class AirbnbDatePickerFooter: UIView { 16 | 17 | var delegate: AirbnbDatePickerFooterDelegate? 18 | var isSaveEnabled: Bool? { 19 | didSet { 20 | if let enabled = isSaveEnabled, enabled { 21 | saveButton.isEnabled = true 22 | saveButton.alpha = 1 23 | } else { 24 | saveButton.isEnabled = false 25 | saveButton.alpha = 0.5 26 | } 27 | } 28 | } 29 | 30 | lazy var saveButton: UIButton = { 31 | let btn = UIButton() 32 | btn.translatesAutoresizingMaskIntoConstraints = false 33 | btn.backgroundColor = Theme.SECONDARY_COLOR 34 | btn.setTitleColor(UIColor.white, for: .normal) 35 | btn.setTitle("Save", for: .normal) 36 | btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14) 37 | btn.addTarget(self, action: #selector(AirbnbDatePickerFooter.handleSave), for: .touchUpInside) 38 | return btn 39 | }() 40 | 41 | override init(frame: CGRect) { 42 | super.init(frame: frame) 43 | 44 | self.backgroundColor = Theme.PRIMARY_COLOR 45 | 46 | addSubview(saveButton) 47 | 48 | saveButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 49 | saveButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 50 | saveButton.heightAnchor.constraint(equalTo: heightAnchor, constant: -20).isActive = true 51 | saveButton.widthAnchor.constraint(equalTo: widthAnchor, constant: -30).isActive = true 52 | } 53 | 54 | required init?(coder aDecoder: NSCoder) { 55 | fatalError("init(coder:) has not been implemented") 56 | } 57 | 58 | @objc func handleSave() { 59 | if let del = delegate { 60 | del.didSave() 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/AirbnbDatePickerMonthHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePickerMonthHeader.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 23/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbDatePickerMonthHeader: BaseCell { 12 | 13 | var monthLabel: UILabel = { 14 | let label = UILabel() 15 | label.translatesAutoresizingMaskIntoConstraints = false 16 | label.textColor = UIColor.white 17 | label.font = UIFont.systemFont(ofSize: 24) 18 | return label 19 | }() 20 | 21 | override func setupViews() { 22 | super.setupViews() 23 | 24 | addSubview(monthLabel) 25 | 26 | monthLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 20).isActive = true 27 | monthLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true 28 | monthLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 29 | monthLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 22/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Calendar-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Calendar-100.png -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "Calendar-100.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "Delete-75 (1).png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Delete.imageset/Delete-75 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Delete.imageset/Delete-75 (1).png -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/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 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/BaseCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseCell.swift 3 | // padstaview 4 | // 5 | // Created by Yonas Stephen on 11/10/16. 6 | // Copyright © 2016 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BaseCell: UICollectionViewCell { 12 | override init(frame: CGRect) { 13 | super.init(frame: frame) 14 | setupViews() 15 | } 16 | 17 | required init?(coder aDecoder: NSCoder) { 18 | fatalError("init(coder:) has not been implemented") 19 | } 20 | 21 | func setupViews() { 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Date.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 24/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Date { 12 | func startOfMonth() -> Date { 13 | return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self)))! 14 | } 15 | 16 | func endOfMonth() -> Date { 17 | return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())! 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeRight 35 | UIInterfaceOrientationLandscapeLeft 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theme.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 23/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Theme { 12 | 13 | static let PRIMARY_COLOR = UIColor(r: 0, g: 132, b: 137) 14 | static let SECONDARY_COLOR = UIColor(r: 65, g: 171, b: 175) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // mychurch-app 4 | // 5 | // Created by Yonas Stephen on 30/6/16. 6 | // Copyright © 2016 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | convenience init(hex: String) { 13 | var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() 14 | if (cString.hasPrefix("#")) { 15 | cString = String(cString.suffix(from: cString.startIndex)) 16 | } 17 | 18 | if ((cString.count) != 6) { 19 | self.init() 20 | } else { 21 | var rgbValue:UInt32 = 0 22 | Scanner(string: cString).scanHexInt32(&rgbValue) 23 | 24 | self.init( 25 | red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, 26 | green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, 27 | blue: CGFloat(rgbValue & 0x0000FF) / 255.0, 28 | alpha: CGFloat(1.0) 29 | ) 30 | } 31 | } 32 | 33 | convenience init(r: CGFloat, g: CGFloat, b: CGFloat) { 34 | self.init(red: CGFloat(r / 255), green: CGFloat(g / 255), blue: CGFloat(b / 255), alpha: CGFloat(1)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/Utility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utility.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 2/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Utility { 12 | 13 | static var calendar: Calendar { 14 | get { 15 | var c = Calendar.current 16 | c.timeZone = TimeZone(abbreviation: "GMT")! 17 | return c 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /airbnb-datepicker/airbnb-datepicker/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 22/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | lazy var datePicker: AirbnbDatePicker = { 14 | let btn = AirbnbDatePicker() 15 | btn.translatesAutoresizingMaskIntoConstraints = false 16 | btn.delegate = self 17 | return btn 18 | }() 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | view.addSubview(datePicker) 24 | 25 | datePicker.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 26 | datePicker.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 27 | datePicker.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -40).isActive = true 28 | datePicker.heightAnchor.constraint(equalToConstant: 50).isActive = true 29 | 30 | } 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbButton.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 13/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbButton: UIButton { 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | 16 | backgroundColor = Theme.TERTIARY_COLOR 17 | setTitleColor(UIColor.white, for: .normal) 18 | layer.cornerRadius = 5 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | fatalError("init(coder:) has not been implemented") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbCategoryTableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbCategoryCell.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 7/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol AirbnbCategoryTableCellDelegate { 12 | func categoryTableCell(_ tableCell: AirbnbCategoryTableCell, collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 13 | } 14 | 15 | class AirbnbCategoryTableCell: BaseTableCell { 16 | 17 | let cellID = "cellID" 18 | var indexPath: IndexPath? 19 | 20 | var layoutSubviewFirstTime: Bool = true 21 | var delegate: AirbnbCategoryTableCellDelegate? 22 | 23 | var items: [AirbnbHome] = [AirbnbHome]() { 24 | didSet { 25 | collectionView.reloadData() 26 | } 27 | } 28 | 29 | var title: String? { 30 | didSet { 31 | titleLabel.text = title 32 | } 33 | } 34 | 35 | var titleLabel: UILabel = { 36 | let view = UILabel() 37 | view.translatesAutoresizingMaskIntoConstraints = false 38 | view.font = UIFont.boldSystemFont(ofSize: 16) 39 | return view 40 | }() 41 | 42 | lazy var collectionView: UICollectionView = { 43 | let layout = UICollectionViewFlowLayout() 44 | layout.scrollDirection = .horizontal 45 | layout.minimumInteritemSpacing = 0 46 | layout.minimumLineSpacing = 0 47 | layout.itemSize = CGSize(width: 280, height: 230) 48 | 49 | let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout) 50 | view.translatesAutoresizingMaskIntoConstraints = false 51 | view.backgroundColor = UIColor.white 52 | view.register(AirbnbHomeItemCell.self, forCellWithReuseIdentifier: self.cellID) 53 | view.showsHorizontalScrollIndicator = false 54 | view.dataSource = self 55 | view.delegate = self 56 | return view 57 | }() 58 | 59 | override func setupViews() { 60 | super.setupViews() 61 | 62 | addSubview(titleLabel) 63 | 64 | titleLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true 65 | titleLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true 66 | titleLabel.widthAnchor.constraint(equalTo: widthAnchor, constant: -30).isActive = true 67 | titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 68 | 69 | addSubview(collectionView) 70 | 71 | collectionView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 72 | collectionView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true 73 | collectionView.heightAnchor.constraint(equalToConstant: 250).isActive = true 74 | collectionView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 75 | 76 | } 77 | 78 | override func layoutSubviews() { 79 | super.layoutSubviews() 80 | 81 | if layoutSubviewFirstTime { 82 | layoutSubviewFirstTime = false 83 | } 84 | } 85 | } 86 | 87 | extension AirbnbCategoryTableCell: UICollectionViewDataSource { 88 | func numberOfSections(in collectionView: UICollectionView) -> Int { 89 | return 1 90 | } 91 | 92 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 93 | return items.count 94 | } 95 | 96 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 97 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! AirbnbHomeItemCell 98 | 99 | cell.home = items[indexPath.item] 100 | 101 | return cell 102 | } 103 | } 104 | 105 | extension AirbnbCategoryTableCell: UICollectionViewDelegate { 106 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 107 | if let del = delegate { 108 | del.categoryTableCell(self, collectionView: collectionView, didSelectItemAt: indexPath) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbContentCategory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbContentCategory.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 7/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbContentCategory { 12 | 13 | var categoryName: String? 14 | var contents: [AirbnbContentThumbnail]? 15 | 16 | init() { 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbContentThumbnail.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbContentThumbnail.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 7/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbContentThumbnail { 12 | var thumbail: UIImage? 13 | var price: Double? 14 | var description: String? 15 | var review: Int? 16 | var type: AirbnbContentThumbnailType? 17 | 18 | init() { 19 | } 20 | } 21 | 22 | enum AirbnbContentThumbnailType: Int { 23 | case Home = 0, Experience, Place 24 | } 25 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbExploreFeedController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbExploreFeedController.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 12/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbExploreFeedController: BaseTableController { 12 | 13 | let cellID = "cellID" 14 | 15 | var categories = ["Just Booked", "Homes", "Experiences", "Featured Destinations"] 16 | let locations = ["Milan", "London", "Stavanger", "Munich"] 17 | lazy var homes: [AirbnbHome] = { 18 | var arr = [AirbnbHome]() 19 | for i in 0..<4 { 20 | let location = self.locations[Int(arc4random_uniform(UInt32(self.locations.count)))] 21 | let home = AirbnbHome(imageName: "home-" + "\(i + 1)", description: "Entire home in \(location)", price: Int(arc4random_uniform(100) + 200), reviewCount: Int(arc4random_uniform(300) + 1), rating: Double(arc4random()) / Double(UINT32_MAX) + 4) 22 | arr.append(home) 23 | } 24 | return arr 25 | }() 26 | 27 | lazy var tableView: UITableView = { 28 | let view = UITableView() 29 | view.translatesAutoresizingMaskIntoConstraints = false 30 | view.dataSource = self 31 | view.delegate = self 32 | view.register(AirbnbCategoryTableCell.self, forCellReuseIdentifier: self.cellID) 33 | view.rowHeight = 300 34 | view.separatorStyle = .none 35 | view.showsVerticalScrollIndicator = false 36 | view.allowsSelection = false 37 | return view 38 | }() 39 | 40 | override func viewDidLoad() { 41 | super.viewDidLoad() 42 | 43 | setupTableView() 44 | } 45 | 46 | func setupTableView() { 47 | view.addSubview(tableView) 48 | 49 | tableView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 50 | tableView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 51 | tableView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true 52 | tableView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true 53 | } 54 | } 55 | 56 | extension AirbnbExploreFeedController: UITableViewDataSource { 57 | 58 | func numberOfSections(in tableView: UITableView) -> Int { 59 | return categories.count 60 | } 61 | 62 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 63 | return 1 64 | } 65 | 66 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 67 | let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! AirbnbCategoryTableCell 68 | 69 | cell.title = categories[indexPath.section] 70 | cell.items = homes 71 | cell.indexPath = indexPath // needed for dismiss animation 72 | 73 | if let parent = parent as? AirbnbCategoryTableCellDelegate { 74 | cell.delegate = parent 75 | } 76 | 77 | return cell 78 | } 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbExploreHomeController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbExploreHomeController.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 23/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbExploreHomeController: BaseTableController { 12 | 13 | let cellID = "cellID" 14 | 15 | let locations = ["Oslo", "Stockholm", "Barcelona", "Madrid", "Copenhagen", "London", "Milan", "Rome", "Hamburg"] 16 | 17 | lazy var items: [AirbnbHome] = { 18 | var arr = [AirbnbHome]() 19 | for i in 5..<11 { 20 | let location = self.locations[Int(arc4random_uniform(UInt32(self.locations.count)))] 21 | let item = AirbnbHome(imageName: "home-\(i)", description: "Entire home in \(location)", price: Int(arc4random_uniform(100) + 200), reviewCount: Int(arc4random_uniform(300) + 1), rating: Double(arc4random()) / Double(UINT32_MAX) + 4) 22 | arr.append(item) 23 | } 24 | return arr 25 | }() 26 | 27 | lazy var tableView: UITableView = { 28 | let view = UITableView() 29 | view.translatesAutoresizingMaskIntoConstraints = false 30 | view.dataSource = self 31 | view.delegate = self 32 | view.register(AirbnbHomeItemTableCell.self, forCellReuseIdentifier: self.cellID) 33 | view.allowsSelection = false 34 | view.rowHeight = 15 + 250 + 20 + 20 + 15 35 | view.separatorStyle = .none 36 | return view 37 | }() 38 | 39 | override func viewDidLoad() { 40 | super.viewDidLoad() 41 | 42 | setupTableView() 43 | } 44 | 45 | func setupTableView() { 46 | view.addSubview(tableView) 47 | 48 | tableView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 49 | tableView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 50 | tableView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true 51 | tableView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true 52 | } 53 | } 54 | 55 | extension AirbnbExploreHomeController: UITableViewDataSource { 56 | func numberOfSections(in tableView: UITableView) -> Int { 57 | return 1 58 | } 59 | 60 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 61 | return items.count 62 | } 63 | 64 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 65 | let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! AirbnbHomeItemTableCell 66 | 67 | //cell.textLabel?.text = "\(indexPath.item)" 68 | cell.home = items[indexPath.row] 69 | 70 | return cell 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbHome.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbHome.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 3/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class AirbnbHome { 12 | var imageName: String 13 | var homeDescription: String 14 | var price: Int 15 | var rating: Double 16 | var reviewCount: Int 17 | 18 | init(imageName: String, description: String, price: Int, reviewCount: Int, rating: Double) { 19 | self.imageName = imageName 20 | self.homeDescription = description 21 | self.price = price 22 | self.reviewCount = reviewCount 23 | self.rating = rating 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbHomeItemCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbHomeItemCell.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 3/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbHomeItemCell: BaseCollectionCell { 12 | 13 | var home: AirbnbHome? { 14 | didSet { 15 | imageView.image = UIImage(named: home!.imageName) 16 | priceLabel.text = "$\(home!.price)" 17 | descriptionLabel.text = home?.homeDescription 18 | reviewCountLabel.text = "\(home!.reviewCount) Reviews" 19 | ratingView.rating = home!.rating 20 | } 21 | } 22 | 23 | var homeDescription: String? { 24 | didSet { 25 | descriptionLabel.text = homeDescription 26 | } 27 | } 28 | 29 | var imageView: UIImageView = { 30 | let view = UIImageView() 31 | view.translatesAutoresizingMaskIntoConstraints = false 32 | view.contentMode = .scaleAspectFill 33 | view.clipsToBounds = true 34 | return view 35 | }() 36 | 37 | var priceLabel: UILabel = { 38 | let view = UILabel() 39 | view.translatesAutoresizingMaskIntoConstraints = false 40 | view.font = UIFont.boldSystemFont(ofSize: 14) 41 | view.textColor = UIColor.black 42 | return view 43 | }() 44 | 45 | var descriptionLabel: UILabel = { 46 | let view = UILabel() 47 | view.translatesAutoresizingMaskIntoConstraints = false 48 | view.font = UIFont.systemFont(ofSize: 14) 49 | view.textColor = UIColor.gray 50 | return view 51 | }() 52 | 53 | var ratingView: AirbnbReview = { 54 | let view = AirbnbReview() 55 | view.translatesAutoresizingMaskIntoConstraints = false 56 | return view 57 | }() 58 | 59 | var reviewCountLabel: UILabel = { 60 | let view = UILabel() 61 | view.translatesAutoresizingMaskIntoConstraints = false 62 | view.font = UIFont.boldSystemFont(ofSize: 10) 63 | view.textColor = UIColor.darkGray 64 | return view 65 | }() 66 | 67 | override func setupViews() { 68 | 69 | addSubview(imageView) 70 | 71 | imageView.topAnchor.constraint(equalTo: topAnchor).isActive = true 72 | imageView.heightAnchor.constraint(equalToConstant: 180).isActive = true 73 | imageView.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true 74 | imageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 75 | 76 | addSubview(priceLabel) 77 | 78 | priceLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 10).isActive = true 79 | priceLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true 80 | priceLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true 81 | priceLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true 82 | 83 | addSubview(descriptionLabel) 84 | 85 | descriptionLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 10).isActive = true 86 | descriptionLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true 87 | descriptionLabel.leftAnchor.constraint(equalTo: priceLabel.rightAnchor).isActive = true 88 | descriptionLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 89 | 90 | addSubview(ratingView) 91 | 92 | ratingView.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 0).isActive = true 93 | ratingView.heightAnchor.constraint(equalToConstant: 20).isActive = true 94 | ratingView.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true 95 | ratingView.widthAnchor.constraint(equalToConstant: ratingView.starSize * CGFloat(ratingView.stars.count)).isActive = true 96 | 97 | addSubview(reviewCountLabel) 98 | 99 | reviewCountLabel.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 0).isActive = true 100 | reviewCountLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true 101 | reviewCountLabel.leftAnchor.constraint(equalTo: ratingView.rightAnchor, constant: 5).isActive = true 102 | reviewCountLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 103 | 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbHomeItemTableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbHomeItemTableCell.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 7/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbHomeItemTableCell: BaseTableCell { 12 | 13 | var home: AirbnbHome? { 14 | didSet { 15 | homeImageView.image = UIImage(named: home!.imageName) 16 | priceLabel.text = "$\(home!.price)" 17 | descriptionLabel.text = home?.homeDescription 18 | reviewLabel.text = "\(home!.reviewCount) Reviews" 19 | } 20 | } 21 | 22 | var homeDescription: String? { 23 | didSet { 24 | descriptionLabel.text = homeDescription 25 | } 26 | } 27 | 28 | var homeImageView: UIImageView = { 29 | let view = UIImageView() 30 | view.translatesAutoresizingMaskIntoConstraints = false 31 | view.contentMode = .scaleAspectFill 32 | view.clipsToBounds = true 33 | return view 34 | }() 35 | 36 | var priceLabel: UILabel = { 37 | let view = UILabel() 38 | view.translatesAutoresizingMaskIntoConstraints = false 39 | view.font = UIFont.boldSystemFont(ofSize: 14) 40 | view.textColor = UIColor.black 41 | return view 42 | }() 43 | 44 | var descriptionLabel: UILabel = { 45 | let view = UILabel() 46 | view.translatesAutoresizingMaskIntoConstraints = false 47 | view.font = UIFont.systemFont(ofSize: 14) 48 | view.textColor = UIColor.gray 49 | return view 50 | }() 51 | 52 | var reviewLabel: UILabel = { 53 | let view = UILabel() 54 | view.translatesAutoresizingMaskIntoConstraints = false 55 | view.font = UIFont.boldSystemFont(ofSize: 10) 56 | view.textColor = UIColor.darkGray 57 | return view 58 | }() 59 | 60 | override func setupViews() { 61 | 62 | addSubview(homeImageView) 63 | 64 | homeImageView.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true 65 | homeImageView.heightAnchor.constraint(equalToConstant: 250).isActive = true 66 | homeImageView.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true 67 | homeImageView.rightAnchor.constraint(equalTo: rightAnchor, constant: -15).isActive = true 68 | 69 | addSubview(priceLabel) 70 | 71 | priceLabel.topAnchor.constraint(equalTo: homeImageView.bottomAnchor, constant: 10).isActive = true 72 | priceLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true 73 | priceLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true 74 | priceLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true 75 | 76 | addSubview(descriptionLabel) 77 | 78 | descriptionLabel.topAnchor.constraint(equalTo: homeImageView.bottomAnchor, constant: 10).isActive = true 79 | descriptionLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true 80 | descriptionLabel.leftAnchor.constraint(equalTo: priceLabel.rightAnchor).isActive = true 81 | descriptionLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -15).isActive = true 82 | 83 | addSubview(reviewLabel) 84 | 85 | reviewLabel.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 0).isActive = true 86 | reviewLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true 87 | reviewLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true 88 | reviewLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -15).isActive = true 89 | 90 | 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbIconLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbIconLabel.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 15/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbIconLabel: UIView { 12 | 13 | var iconView: UIImageView = { 14 | let view = UIImageView() 15 | view.translatesAutoresizingMaskIntoConstraints = false 16 | view.contentMode = .scaleAspectFill 17 | return view 18 | }() 19 | 20 | var labelView: UILabel = { 21 | let view = UILabel() 22 | view.translatesAutoresizingMaskIntoConstraints = false 23 | view.font = UIFont.systemFont(ofSize: 16) 24 | view.textColor = UIColor.black 25 | return view 26 | }() 27 | 28 | override init(frame: CGRect) { 29 | super.init(frame: frame) 30 | 31 | addSubview(labelView) 32 | 33 | labelView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 34 | labelView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 35 | labelView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true 36 | labelView.heightAnchor.constraint(equalToConstant: 20).isActive = true 37 | 38 | addSubview(iconView) 39 | 40 | iconView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 41 | iconView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 42 | iconView.bottomAnchor.constraint(equalTo: labelView.topAnchor).isActive = true 43 | iconView.topAnchor.constraint(equalTo: topAnchor).isActive = true 44 | 45 | } 46 | 47 | required init?(coder aDecoder: NSCoder) { 48 | fatalError("init(coder:) has not been implemented") 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbMainController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbMainController.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 3/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbMainController: UITabBarController { 12 | 13 | let AIRBNB_COLOR = UIColor(r: 255, g: 90, b: 95) 14 | 15 | var tabBarHeight: CGFloat = 60 16 | var tabBarTextAttributesNormal = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 8), NSAttributedStringKey.foregroundColor: UIColor.black] 17 | var tabBarTextAttributesSelected = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 8), NSAttributedStringKey.foregroundColor: UIColor(r: 255, g: 90, b: 95)] 18 | 19 | var tabBarSeparator: UIView = { 20 | let view = UIView() 21 | view.translatesAutoresizingMaskIntoConstraints = false 22 | view.backgroundColor = UIColor.lightGray 23 | return view 24 | }() 25 | 26 | var exploreTabItem: UITabBarItem = { 27 | let icon = UIImage(named: "Search") 28 | let tab = UITabBarItem(title: "EXPLORE", image: icon?.tint(with: .black).withRenderingMode(.alwaysOriginal), selectedImage: icon) 29 | tab.titlePositionAdjustment = UIOffsetMake(0, -5) 30 | return tab 31 | }() 32 | 33 | lazy var exploreController: AirbnbExploreController = { 34 | let controller = AirbnbExploreController() 35 | controller.tabBarItem = self.exploreTabItem 36 | return controller 37 | }() 38 | 39 | var savedTabItem: UITabBarItem = { 40 | let icon = UIImage(named: "Heart") 41 | let tab = UITabBarItem(title: "SAVED", image: icon?.tint(with: .black).withRenderingMode(.alwaysOriginal), selectedImage: icon) 42 | tab.titlePositionAdjustment = UIOffsetMake(0, -5) 43 | return tab 44 | }() 45 | 46 | lazy var savedController: UIViewController = { 47 | let controller = UIViewController() 48 | controller.tabBarItem = self.savedTabItem 49 | return controller 50 | }() 51 | 52 | var tripsTabItem: UITabBarItem = { 53 | let icon = UIImage(named: "Bed") 54 | let tab = UITabBarItem(title: "TRIPS", image: icon?.tint(with: .black).withRenderingMode(.alwaysOriginal), selectedImage: icon) 55 | tab.titlePositionAdjustment = UIOffsetMake(0, -5) 56 | return tab 57 | }() 58 | 59 | lazy var tripsController: AirbnbMapController = { 60 | let controller = AirbnbMapController() 61 | controller.tabBarItem = self.tripsTabItem 62 | return controller 63 | }() 64 | 65 | var inboxTabItem: UITabBarItem = { 66 | let icon = UIImage(named: "Message") 67 | let tab = UITabBarItem(title: "INBOX", image: icon?.tint(with: .black).withRenderingMode(.alwaysOriginal), selectedImage: icon) 68 | tab.titlePositionAdjustment = UIOffsetMake(0, -5) 69 | return tab 70 | }() 71 | 72 | lazy var inboxController: UIViewController = { 73 | let controller = UIViewController() 74 | controller.tabBarItem = self.inboxTabItem 75 | return controller 76 | }() 77 | 78 | var profileTabItem: UITabBarItem = { 79 | let icon = UIImage(named: "Profile") 80 | let tab = UITabBarItem(title: "PROFILE", image: icon?.tint(with: .black).withRenderingMode(.alwaysOriginal), selectedImage: icon) 81 | tab.titlePositionAdjustment = UIOffsetMake(0, -5) 82 | return tab 83 | }() 84 | 85 | lazy var profileController: UIViewController = { 86 | let controller = UIViewController() 87 | controller.tabBarItem = self.profileTabItem 88 | return controller 89 | }() 90 | 91 | override func viewDidLoad() { 92 | super.viewDidLoad() 93 | 94 | viewControllers = [exploreController, savedController, tripsController, inboxController, profileController] 95 | delegate = self 96 | 97 | setupTabBar() 98 | } 99 | 100 | override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { 101 | return selectedViewController?.preferredStatusBarUpdateAnimation ?? .fade 102 | } 103 | 104 | override var preferredStatusBarStyle: UIStatusBarStyle { 105 | return selectedViewController?.preferredStatusBarStyle ?? .default 106 | } 107 | 108 | override var prefersStatusBarHidden: Bool { 109 | return selectedViewController?.prefersStatusBarHidden ?? false 110 | } 111 | 112 | func setupTabBar() { 113 | 114 | // add separator line 115 | tabBar.clipsToBounds = true // hide default top border 116 | tabBar.addSubview(tabBarSeparator) 117 | 118 | tabBarSeparator.widthAnchor.constraint(equalTo: tabBar.widthAnchor).isActive = true 119 | tabBarSeparator.heightAnchor.constraint(equalToConstant: 1).isActive = true 120 | tabBarSeparator.centerXAnchor.constraint(equalTo: tabBar.centerXAnchor).isActive = true 121 | tabBarSeparator.topAnchor.constraint(equalTo: tabBar.topAnchor).isActive = true 122 | 123 | tabBar.tintColor = AIRBNB_COLOR 124 | tabBar.barTintColor = UIColor.white 125 | tabBar.isTranslucent = false 126 | 127 | UITabBarItem.appearance().setTitleTextAttributes(tabBarTextAttributesNormal, for: .normal) 128 | UITabBarItem.appearance().setTitleTextAttributes(tabBarTextAttributesSelected, for: .selected) 129 | } 130 | 131 | } 132 | 133 | extension AirbnbMainController: UITabBarControllerDelegate { 134 | 135 | } 136 | 137 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbMapController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbMapController.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 20/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | 12 | class AirbnbMapController: UIViewController { 13 | 14 | let cellID = "cellID" 15 | 16 | let locations = ["Oslo", "Stockholm", "Barcelona", "Madrid", "Copenhagen", "London", "Milan", "Rome", "Hamburg"] 17 | 18 | lazy var items: [AirbnbHome] = { 19 | var arr = [AirbnbHome]() 20 | for i in 5..<11 { 21 | let location = self.locations[Int(arc4random_uniform(UInt32(self.locations.count)))] 22 | let item = AirbnbHome(imageName: "home-\(i)", description: "Entire home in \(location)", price: Int(arc4random_uniform(100) + 200), reviewCount: Int(arc4random_uniform(300) + 1), rating: Double(arc4random()) / Double(UINT32_MAX) + 4) 23 | arr.append(item) 24 | } 25 | return arr 26 | }() 27 | 28 | var mapView: MKMapView = { 29 | let view = MKMapView() 30 | view.translatesAutoresizingMaskIntoConstraints = false 31 | return view 32 | }() 33 | 34 | lazy var thumbnailCollectionView: UICollectionView = { 35 | let layout = UICollectionViewFlowLayout() 36 | layout.scrollDirection = .horizontal 37 | layout.minimumInteritemSpacing = 0 38 | layout.minimumLineSpacing = 0 39 | layout.itemSize = CGSize(width: 200, height: 185) 40 | 41 | let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout) 42 | view.translatesAutoresizingMaskIntoConstraints = false 43 | view.backgroundColor = UIColor.white 44 | view.register(AirbnbMapItemCell.self, forCellWithReuseIdentifier: self.cellID) 45 | view.showsHorizontalScrollIndicator = false 46 | view.dataSource = self 47 | return view 48 | }() 49 | 50 | override func viewDidLoad() { 51 | super.viewDidLoad() 52 | 53 | view.addSubview(thumbnailCollectionView) 54 | 55 | thumbnailCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true 56 | thumbnailCollectionView.heightAnchor.constraint(equalToConstant: 185).isActive = true 57 | thumbnailCollectionView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 58 | thumbnailCollectionView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true 59 | 60 | view.addSubview(mapView) 61 | 62 | mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true 63 | mapView.bottomAnchor.constraint(equalTo: thumbnailCollectionView.topAnchor).isActive = true 64 | mapView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 65 | mapView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true 66 | } 67 | 68 | } 69 | 70 | extension AirbnbMapController: UICollectionViewDataSource { 71 | func numberOfSections(in collectionView: UICollectionView) -> Int { 72 | return 1 73 | } 74 | 75 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 76 | return items.count 77 | } 78 | 79 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 80 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! AirbnbMapItemCell 81 | 82 | cell.home = items[indexPath.item] 83 | 84 | return cell 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbMapItemCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbMapItemCell.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 3/7/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbMapItemCell: BaseCollectionCell { 12 | 13 | var home: AirbnbHome? { 14 | didSet { 15 | imageView.image = UIImage(named: home!.imageName) 16 | priceLabel.text = "$\(home!.price)" 17 | descriptionLabel.text = home?.homeDescription 18 | reviewCountLabel.text = "\(home!.reviewCount) Reviews" 19 | ratingView.rating = home!.rating 20 | } 21 | } 22 | 23 | var homeDescription: String? { 24 | didSet { 25 | descriptionLabel.text = homeDescription 26 | } 27 | } 28 | 29 | var imageView: UIImageView = { 30 | let view = UIImageView() 31 | view.translatesAutoresizingMaskIntoConstraints = false 32 | view.contentMode = .scaleAspectFill 33 | view.clipsToBounds = true 34 | return view 35 | }() 36 | 37 | var priceLabel: UILabel = { 38 | let view = UILabel() 39 | view.translatesAutoresizingMaskIntoConstraints = false 40 | view.font = UIFont.boldSystemFont(ofSize: 14) 41 | view.textColor = UIColor.black 42 | return view 43 | }() 44 | 45 | var descriptionLabel: UILabel = { 46 | let view = UILabel() 47 | view.translatesAutoresizingMaskIntoConstraints = false 48 | view.font = UIFont.systemFont(ofSize: 14) 49 | view.textColor = UIColor.gray 50 | return view 51 | }() 52 | 53 | var ratingView: AirbnbReview = { 54 | let view = AirbnbReview() 55 | view.translatesAutoresizingMaskIntoConstraints = false 56 | return view 57 | }() 58 | 59 | var reviewCountLabel: UILabel = { 60 | let view = UILabel() 61 | view.translatesAutoresizingMaskIntoConstraints = false 62 | view.font = UIFont.boldSystemFont(ofSize: 10) 63 | view.textColor = UIColor.darkGray 64 | return view 65 | }() 66 | 67 | override func setupViews() { 68 | 69 | addSubview(imageView) 70 | 71 | imageView.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true 72 | imageView.heightAnchor.constraint(equalToConstant: 120).isActive = true 73 | imageView.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true 74 | imageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 75 | 76 | addSubview(priceLabel) 77 | 78 | priceLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 5).isActive = true 79 | priceLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true 80 | priceLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true 81 | priceLabel.widthAnchor.constraint(equalToConstant: 40).isActive = true 82 | 83 | addSubview(descriptionLabel) 84 | 85 | descriptionLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 5).isActive = true 86 | descriptionLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true 87 | descriptionLabel.leftAnchor.constraint(equalTo: priceLabel.rightAnchor).isActive = true 88 | descriptionLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 89 | 90 | addSubview(ratingView) 91 | 92 | ratingView.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 0).isActive = true 93 | ratingView.heightAnchor.constraint(equalToConstant: 20).isActive = true 94 | ratingView.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true 95 | ratingView.widthAnchor.constraint(equalToConstant: ratingView.starSize * CGFloat(ratingView.stars.count)).isActive = true 96 | 97 | addSubview(reviewCountLabel) 98 | 99 | reviewCountLabel.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 0).isActive = true 100 | reviewCountLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true 101 | reviewCountLabel.leftAnchor.constraint(equalTo: ratingView.rightAnchor, constant: 5).isActive = true 102 | reviewCountLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 103 | 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbPageTabItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbPageTabItem.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 13/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol AirbnbPageTabItemDelegate { 12 | func didSelect(tabItem: AirbnbPageTabItem, completion: (() -> Void)?) 13 | } 14 | 15 | class AirbnbPageTabItem: BasePageTabItemView { 16 | 17 | var delegate: AirbnbPageTabItemDelegate? 18 | 19 | var title: String = "" { 20 | didSet { 21 | titleButton.setTitle(title.uppercased(), for: .normal) 22 | } 23 | } 24 | var titleColor: UIColor = UIColor.white { 25 | didSet { 26 | titleButton.setTitleColor(titleColor, for: .normal) 27 | } 28 | } 29 | 30 | var selectedTitleColor: UIColor = UIColor.white { 31 | didSet { 32 | if isSelected { 33 | titleButton.setTitleColor(selectedTitleColor, for: .normal) 34 | } 35 | } 36 | } 37 | 38 | var isSelected: Bool = false { 39 | didSet { 40 | if isSelected { 41 | 42 | } else { 43 | 44 | } 45 | } 46 | } 47 | 48 | lazy var titleButton: UIButton = { 49 | let view = UIButton() 50 | view.translatesAutoresizingMaskIntoConstraints = false 51 | view.setTitleColor(self.titleColor, for: .normal) 52 | view.titleLabel?.textAlignment = .center 53 | view.titleLabel?.font = UIFont.boldSystemFont(ofSize: 12) 54 | view.addTarget(self, action: #selector(AirbnbPageTabItem.didSelect), for: .touchUpInside) 55 | return view 56 | }() 57 | 58 | var titleLabelWidthAnchor: NSLayoutConstraint? 59 | 60 | override func setupViews() { 61 | super.setupViews() 62 | 63 | addSubview(titleButton) 64 | 65 | titleButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 66 | titleButton.topAnchor.constraint(equalTo: topAnchor).isActive = true 67 | titleButton.heightAnchor.constraint(equalTo: heightAnchor).isActive = true 68 | titleButton.widthAnchor.constraint(equalTo: widthAnchor, constant: -30).isActive = true 69 | } 70 | 71 | @objc func didSelect() { 72 | if let handler = delegate { 73 | handler.didSelect(tabItem: self, completion: nil) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/AirbnbReview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbReview.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 13/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbReview: UIView { 12 | 13 | var starSize: CGFloat = 12 14 | 15 | var rating: Double = 0 { 16 | didSet { 17 | var val = rating 18 | for i in 0..= 0.75 { 20 | stars[i].type = .full 21 | val -= 1 22 | } else if val >= 0.25 { 23 | stars[i].type = .halfEmpty 24 | val -= 0.5 25 | } else { 26 | stars[i].type = .empty 27 | } 28 | } 29 | } 30 | } 31 | 32 | var stars: [AirbnbStar] = { 33 | var arr = [AirbnbStar]() 34 | for _ in 0..<5 { 35 | let view = AirbnbStar() 36 | view.translatesAutoresizingMaskIntoConstraints = false 37 | arr.append(view) 38 | } 39 | return arr 40 | }() 41 | 42 | override init(frame: CGRect) { 43 | super.init(frame: frame) 44 | 45 | for i in 0.. Bool { 18 | // Override point for customization after application launch. 19 | UIApplication.shared.statusBarStyle = .lightContent 20 | 21 | window = UIWindow(frame: UIScreen.main.bounds) 22 | window?.rootViewController = AirbnbMainController() 23 | window?.makeKeyAndVisible() 24 | return true 25 | } 26 | 27 | func applicationWillResignActive(_ application: UIApplication) { 28 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 29 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 30 | } 31 | 32 | func applicationDidEnterBackground(_ application: UIApplication) { 33 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | func applicationWillEnterForeground(_ application: UIApplication) { 38 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 39 | } 40 | 41 | func applicationDidBecomeActive(_ application: UIApplication) { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | func applicationWillTerminate(_ application: UIApplication) { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Back.imageset/Back-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Back.imageset/Back-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Back.imageset/Back-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Back.imageset/Back-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Back.imageset/Back-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Back.imageset/Back-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Back-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Back-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Back-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Bed.imageset/Bed-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Bed.imageset/Bed-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Bed.imageset/Bed-50 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Bed.imageset/Bed-50 (1).png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Bed.imageset/Bed-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Bed.imageset/Bed-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Bed.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Bed-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Bed-50 (1).png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Bed-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Collapse.imageset/Collapse Arrow-25 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Collapse.imageset/Collapse Arrow-25 (1).png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Collapse.imageset/Collapse Arrow-50 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Collapse.imageset/Collapse Arrow-50 (1).png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Collapse.imageset/Collapse Arrow-75 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Collapse.imageset/Collapse Arrow-75 (1).png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Collapse.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Collapse Arrow-25 (1).png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Collapse Arrow-50 (1).png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Collapse Arrow-75 (1).png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Door.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Door Opened-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Door Opened-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Door Opened-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Door.imageset/Door Opened-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Door.imageset/Door Opened-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Door.imageset/Door Opened-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Door.imageset/Door Opened-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Door.imageset/Door Opened-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Door.imageset/Door Opened-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Expand.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Expand Arrow-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Expand Arrow-50 (1).png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Expand Arrow-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Expand.imageset/Expand Arrow-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Expand.imageset/Expand Arrow-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Expand.imageset/Expand Arrow-50 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Expand.imageset/Expand Arrow-50 (1).png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Expand.imageset/Expand Arrow-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Expand.imageset/Expand Arrow-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Globe.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Globe Earth-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Globe Earth-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Globe Earth-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Globe.imageset/Globe Earth-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Globe.imageset/Globe Earth-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Globe.imageset/Globe Earth-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Globe.imageset/Globe Earth-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Globe.imageset/Globe Earth-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Globe.imageset/Globe Earth-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Heart.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Hearts-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Hearts-50 (1).png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Hearts-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Heart.imageset/Hearts-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Heart.imageset/Hearts-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Heart.imageset/Hearts-50 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Heart.imageset/Hearts-50 (1).png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Heart.imageset/Hearts-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Heart.imageset/Hearts-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Message.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Message-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Message-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Message-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Message.imageset/Message-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Message.imageset/Message-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Message.imageset/Message-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Message.imageset/Message-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Message.imageset/Message-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Message.imageset/Message-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Profile.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "User-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "User-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "User-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Profile.imageset/User-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Profile.imageset/User-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Profile.imageset/User-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Profile.imageset/User-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Profile.imageset/User-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Profile.imageset/User-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Search-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Search-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Search-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Search.imageset/Search-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Search.imageset/Search-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Search.imageset/Search-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Search.imageset/Search-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Search.imageset/Search-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Search.imageset/Search-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Shower.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Shower and Tub-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Shower and Tub-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Shower and Tub-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Shower.imageset/Shower and Tub-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Shower.imageset/Shower and Tub-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Shower.imageset/Shower and Tub-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Shower.imageset/Shower and Tub-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Shower.imageset/Shower and Tub-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Shower.imageset/Shower and Tub-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Empty.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Star-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Star-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Star-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Empty.imageset/Star-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Star Empty.imageset/Star-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Empty.imageset/Star-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Star Empty.imageset/Star-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Empty.imageset/Star-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Star Empty.imageset/Star-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Star Filled-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Star Filled-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Star Filled-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Filled.imageset/Star Filled-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Star Filled.imageset/Star Filled-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Filled.imageset/Star Filled-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Star Filled.imageset/Star Filled-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Filled.imageset/Star Filled-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Star Filled.imageset/Star Filled-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Half Empty.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Star Half Empty-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Star Half Empty-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Star Half Empty-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Half Empty.imageset/Star Half Empty-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Star Half Empty.imageset/Star Half Empty-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Half Empty.imageset/Star Half Empty-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Star Half Empty.imageset/Star Half Empty-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Star Half Empty.imageset/Star Half Empty-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Star Half Empty.imageset/Star Half Empty-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Upload.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Upload-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Upload-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Upload-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Upload.imageset/Upload-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Upload.imageset/Upload-25.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Upload.imageset/Upload-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Upload.imageset/Upload-50.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/Upload.imageset/Upload-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/Upload.imageset/Upload-75.png -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-1.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-1.imageset/home-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-1.imageset/home-1.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-10.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-10.imageset/home-10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-10.imageset/home-10.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-2.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-2.imageset/home-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-2.imageset/home-2.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-3.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-3.imageset/home-3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-3.imageset/home-3.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-4.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-4.imageset/home-4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-4.imageset/home-4.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-5.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-5.imageset/home-5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-5.imageset/home-5.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-6.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-6.imageset/home-6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-6.imageset/home-6.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-7.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-7.imageset/home-7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-7.imageset/home-7.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-8.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-8.imageset/home-8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-8.imageset/home-8.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "home-9.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/home-9.imageset/home-9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/home-9.imageset/home-9.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/profpic.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "profpic.jpeg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Assets.xcassets/profpic.imageset/profpic.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-main/airbnb-main/Assets.xcassets/profpic.imageset/profpic.jpeg -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/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 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Base.lproj/Main.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 | 30 | 31 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/BaseCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseCollectionCell.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 3/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BaseCollectionCell: UICollectionViewCell { 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | setupViews() 16 | } 17 | 18 | required init?(coder aDecoder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | func setupViews() { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/BasePageTabItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BasePageTabView.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 13/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BasePageTabItemView: UIView { 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | 16 | } 17 | 18 | override func layoutSubviews() { 19 | setupViews() 20 | } 21 | 22 | required init?(coder aDecoder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | func setupViews() { 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/BaseTableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseCell.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 7/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BaseTableCell: UITableViewCell { 12 | 13 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 14 | super.init(style: style, reuseIdentifier: reuseIdentifier) 15 | setupViews() 16 | } 17 | 18 | required init?(coder aDecoder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | func setupViews() { 23 | 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/BaseTableController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseTableController.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 13/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol BaseTableControllerDelegate { 12 | var headerViewHeightConstraint: NSLayoutConstraint? { get set } 13 | var headerView: AirbnbExploreHeaderView { get set } 14 | var maxHeaderHeight: CGFloat { get } 15 | var midHeaderHeight: CGFloat { get } 16 | var minHeaderHeight: CGFloat { get } 17 | 18 | func layoutViews() 19 | func updateStatusBar() 20 | } 21 | 22 | class BaseTableController: UIViewController { 23 | var headerDelegate: BaseTableControllerDelegate! 24 | var previousScrollOffset: CGFloat = 0 25 | 26 | var isHiddenStatusBar: Bool = false 27 | var statusBarStyle: UIStatusBarStyle = .lightContent 28 | 29 | override func didMove(toParentViewController parent: UIViewController?) { 30 | if let del = parent as? BaseTableControllerDelegate { 31 | self.headerDelegate = del 32 | } 33 | } 34 | 35 | override func viewWillDisappear(_ animated: Bool) { 36 | super.viewWillDisappear(animated) 37 | UIApplication.shared.statusBarStyle = .default 38 | } 39 | 40 | override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { 41 | return .fade 42 | } 43 | 44 | override var preferredStatusBarStyle: UIStatusBarStyle { 45 | return statusBarStyle 46 | } 47 | 48 | override var prefersStatusBarHidden: Bool { 49 | return isHiddenStatusBar 50 | } 51 | 52 | func setStatusBarHidden(_ isHidden: Bool, withStyle style: UIStatusBarStyle) { 53 | if isHidden != isHiddenStatusBar || statusBarStyle != style { 54 | statusBarStyle = style 55 | isHiddenStatusBar = isHidden 56 | 57 | UIView.animate(withDuration: 0.5, animations: { 58 | self.headerDelegate.updateStatusBar() 59 | }) 60 | } 61 | } 62 | 63 | func updateStatusBar() { 64 | let curHeight = headerDelegate.headerViewHeightConstraint!.constant 65 | if curHeight == headerDelegate.minHeaderHeight { 66 | setStatusBarHidden(false, withStyle: .default) 67 | } else if curHeight > headerDelegate.minHeaderHeight, curHeight < headerDelegate.midHeaderHeight { 68 | setStatusBarHidden(true, withStyle: .lightContent) 69 | } else { 70 | setStatusBarHidden(false, withStyle: .lightContent) 71 | } 72 | } 73 | } 74 | 75 | extension BaseTableController: UITableViewDelegate { 76 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 77 | let absoluteTop: CGFloat = 0 78 | let absoluteBottom: CGFloat = max(0, scrollView.contentSize.height - scrollView.frame.height) 79 | 80 | let scrollDif = scrollView.contentOffset.y - previousScrollOffset 81 | let isScrollUp = scrollDif < 0 && scrollView.contentOffset.y < absoluteBottom // swipe down - header expands 82 | let isScrollDown = scrollDif > 0 && scrollView.contentOffset.y > absoluteTop // swipe up - header shrinks 83 | 84 | var newHeight = headerDelegate.headerViewHeightConstraint!.constant 85 | if isScrollUp { 86 | newHeight = min(headerDelegate.maxHeaderHeight, (headerDelegate.headerViewHeightConstraint!.constant + abs(scrollDif))) 87 | } else if isScrollDown { 88 | newHeight = max(headerDelegate.minHeaderHeight, (headerDelegate.headerViewHeightConstraint!.constant - abs(scrollDif))) 89 | } 90 | 91 | if newHeight != headerDelegate.headerViewHeightConstraint!.constant { 92 | headerDelegate.headerViewHeightConstraint?.constant = newHeight 93 | headerDelegate.headerView.updateHeader(newHeight: newHeight, offset: scrollDif) 94 | setScrollPosition(scrollView, toPosition: previousScrollOffset) 95 | updateStatusBar() 96 | } 97 | 98 | previousScrollOffset = scrollView.contentOffset.y 99 | } 100 | 101 | func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 102 | scrollViewDidStopScrolling(scrollView) 103 | } 104 | 105 | func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { 106 | if !decelerate { 107 | scrollViewDidStopScrolling(scrollView) 108 | } 109 | } 110 | 111 | func setScrollPosition(_ scrollView: UIScrollView, toPosition position: CGFloat) { 112 | scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: position) 113 | } 114 | 115 | func scrollViewDidStopScrolling(_ scrollView: UIScrollView) { 116 | let curHeight = headerDelegate.headerViewHeightConstraint!.constant 117 | if curHeight < headerDelegate.midHeaderHeight { 118 | setHeaderHeight(scrollView, height: headerDelegate.minHeaderHeight) 119 | } else if curHeight < headerDelegate.maxHeaderHeight - headerDelegate.headerView.headerInputHeight { 120 | setHeaderHeight(scrollView, height: headerDelegate.midHeaderHeight) 121 | } else { 122 | setHeaderHeight(scrollView, height: headerDelegate.maxHeaderHeight) 123 | } 124 | 125 | updateStatusBar() 126 | } 127 | 128 | func setHeaderHeight(_ scrollView: UIScrollView, height: CGFloat) { 129 | self.headerDelegate.layoutViews() 130 | UIView.animate(withDuration: 0.2, animations: { 131 | self.headerDelegate.headerViewHeightConstraint?.constant = height 132 | self.headerDelegate.headerView.updateHeader(newHeight: height, offset: scrollView.contentOffset.y - self.previousScrollOffset) 133 | self.headerDelegate.layoutViews() 134 | }) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UIViewControllerBasedStatusBarAppearance 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 13/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension String { 12 | func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat { 13 | let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude) 14 | let boundingBox = (self as NSString).boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil) 15 | return boundingBox.height 16 | } 17 | 18 | func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat { 19 | let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height) 20 | let boundingBox = (self as NSString).boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil) 21 | return boundingBox.width 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theme.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 29/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Theme { 12 | 13 | static let PRIMARY_COLOR = UIColor(r: 0, g: 132, b: 137) 14 | static let PRIMARY_DARK_COLOR = UIColor(r: 15, g: 114, b: 118) 15 | static let SECONDARY_COLOR = UIColor(r: 65, g: 171, b: 175) 16 | static let TERTIARY_COLOR = UIColor(r: 255, g: 90, b: 95) 17 | 18 | static let INPUT_COLOR = UIColor(r: 37, g: 145, b: 146) 19 | static let DARK_GRAY = UIColor(r: 75, g: 75, b: 75) 20 | static let GRAY = UIColor(r: 150, g: 150, b: 150) 21 | static let LIGHT_GRAY = UIColor(r: 236, g: 236, b: 236) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 9/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | convenience init(hex: String) { 13 | var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() 14 | if (cString.hasPrefix("#")) { 15 | cString = String(cString.suffix(from: cString.startIndex)) 16 | } 17 | 18 | if ((cString.count) != 6) { 19 | self.init() 20 | } else { 21 | var rgbValue:UInt32 = 0 22 | Scanner(string: cString).scanHexInt32(&rgbValue) 23 | 24 | self.init( 25 | red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, 26 | green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, 27 | blue: CGFloat(rgbValue & 0x0000FF) / 255.0, 28 | alpha: CGFloat(1.0) 29 | ) 30 | } 31 | } 32 | 33 | convenience init(r: CGFloat, g: CGFloat, b: CGFloat) { 34 | self.init(red: CGFloat(r / 255), green: CGFloat(g / 255), blue: CGFloat(b / 255), alpha: CGFloat(1)) 35 | } 36 | 37 | static func fade(fromColor: UIColor, toColor: UIColor, withPercentage: CGFloat) -> UIColor { 38 | 39 | var fromRed: CGFloat = 0.0 40 | var fromGreen: CGFloat = 0.0 41 | var fromBlue: CGFloat = 0.0 42 | var fromAlpha: CGFloat = 0.0 43 | 44 | fromColor.getRed(&fromRed, green: &fromGreen, blue: &fromBlue, alpha: &fromAlpha) 45 | 46 | var toRed: CGFloat = 0.0 47 | var toGreen: CGFloat = 0.0 48 | var toBlue: CGFloat = 0.0 49 | var toAlpha: CGFloat = 0.0 50 | 51 | toColor.getRed(&toRed, green: &toGreen, blue: &toBlue, alpha: &toAlpha) 52 | 53 | //calculate the actual RGBA values of the fade colour 54 | let red = (toRed - fromRed) * withPercentage + fromRed; 55 | let green = (toGreen - fromGreen) * withPercentage + fromGreen; 56 | let blue = (toBlue - fromBlue) * withPercentage + fromBlue; 57 | let alpha = (toAlpha - fromAlpha) * withPercentage + fromAlpha; 58 | 59 | // return the fade colour 60 | return UIColor(red: red, green: green, blue: blue, alpha: alpha) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/UIImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 12/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImage { 12 | func tint(with color: UIColor) -> UIImage { 13 | var image = withRenderingMode(.alwaysTemplate) 14 | UIGraphicsBeginImageContextWithOptions(size, false, scale) 15 | color.set() 16 | 17 | image.draw(in: CGRect(origin: .zero, size: size)) 18 | image = UIGraphicsGetImageFromCurrentImageContext()! 19 | UIGraphicsEndImageContext() 20 | return image 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/UITabBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITabBar.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 12/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UITabBar { 12 | 13 | override open func sizeThatFits(_ size: CGSize) -> CGSize { 14 | var sizeThatFits = super.sizeThatFits(size) 15 | sizeThatFits.height = 55 // adjust your size here 16 | return sizeThatFits 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /airbnb-main/airbnb-main/VerticalAlignLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VerticalAlignLabel.swift 3 | // airbnb-main 4 | // 5 | // Created by Yonas Stephen on 13/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class VerticalAlignLabel: UILabel { 12 | enum VerticalAlignment { 13 | case top 14 | case middle 15 | case bottom 16 | } 17 | 18 | var verticalAlignment : VerticalAlignment = .top { 19 | didSet { 20 | setNeedsDisplay() 21 | } 22 | } 23 | 24 | override public func textRect(forBounds bounds: CGRect, limitedToNumberOfLines: Int) -> CGRect { 25 | let rect = super.textRect(forBounds: bounds, limitedToNumberOfLines: limitedToNumberOfLines) 26 | 27 | if UIView.userInterfaceLayoutDirection(for: .unspecified) == .rightToLeft { 28 | switch verticalAlignment { 29 | case .top: 30 | return CGRect(x: self.bounds.size.width - rect.size.width, y: bounds.origin.y, width: rect.size.width, height: rect.size.height) 31 | case .middle: 32 | return CGRect(x: self.bounds.size.width - rect.size.width, y: bounds.origin.y + (bounds.size.height - rect.size.height) / 2, width: rect.size.width, height: rect.size.height) 33 | case .bottom: 34 | return CGRect(x: self.bounds.size.width - rect.size.width, y: bounds.origin.y + (bounds.size.height - rect.size.height), width: rect.size.width, height: rect.size.height) 35 | } 36 | } else { 37 | switch verticalAlignment { 38 | case .top: 39 | return CGRect(x: bounds.origin.x, y: bounds.origin.y, width: rect.size.width, height: rect.size.height) 40 | case .middle: 41 | return CGRect(x: bounds.origin.x, y: bounds.origin.y + (bounds.size.height - rect.size.height) / 2, width: rect.size.width, height: rect.size.height) 42 | case .bottom: 43 | return CGRect(x: bounds.origin.x, y: bounds.origin.y + (bounds.size.height - rect.size.height), width: rect.size.width, height: rect.size.height) 44 | } 45 | } 46 | } 47 | 48 | override public func drawText(in rect: CGRect) { 49 | let r = self.textRect(forBounds: rect, limitedToNumberOfLines: self.numberOfLines) 50 | super.drawText(in: r) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/AirbnbOccupantFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbOccupantFilter.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 4/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbOccupantFilter: UIView { 12 | 13 | public var delegate: UIViewController? 14 | 15 | var adultCount: Int = 1 16 | var childrenCount: Int = 0 17 | var infantCount: Int = 0 18 | var hasPet: Bool = false 19 | 20 | // MARK: - View Components 21 | 22 | var occupantInputButton: UIButton = { 23 | let btn = UIButton() 24 | btn.translatesAutoresizingMaskIntoConstraints = false 25 | btn.backgroundColor = Theme.PRIMARY_COLOR 26 | btn.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left 27 | btn.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) 28 | btn.adjustsImageWhenHighlighted = false 29 | btn.addTarget(self, action: #selector(AirbnbOccupantFilter.showOccupantFilter), for: .touchUpInside) 30 | 31 | let img = UIImage(named: "Family", in: Bundle(for: AirbnbOccupantFilter.self), compatibleWith: nil) 32 | btn.setImage(img, for: .normal) 33 | btn.imageView?.contentMode = .scaleAspectFit 34 | 35 | btn.setTitle("1 guest", for: .normal) 36 | btn.setTitleColor(UIColor.white, for: .normal) 37 | btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 15) 38 | btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0) 39 | btn.titleLabel?.lineBreakMode = .byTruncatingTail 40 | return btn 41 | }() 42 | 43 | // MARK: - Initializations 44 | 45 | override init(frame: CGRect) { 46 | super.init(frame: frame) 47 | setupViews() 48 | } 49 | 50 | required init?(coder aDecoder: NSCoder) { 51 | fatalError("init(coder:) has not been implemented") 52 | } 53 | 54 | // MARK: - View Setups 55 | 56 | func setupViews() { 57 | addSubview(occupantInputButton) 58 | 59 | occupantInputButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 60 | occupantInputButton.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 61 | occupantInputButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 62 | occupantInputButton.heightAnchor.constraint(equalTo: heightAnchor).isActive = true 63 | 64 | } 65 | 66 | // MARK: - Event Handlers 67 | 68 | func showOccupantFilter() { 69 | let occupantController = AirbnbOccupantFilterController(adultCount: adultCount, childrenCount: childrenCount, infantCount: infantCount, hasPet: hasPet) 70 | occupantController.delegate = self 71 | let navigationController = UINavigationController(rootViewController: occupantController) 72 | delegate?.present(navigationController, animated: true, completion: nil) 73 | } 74 | } 75 | 76 | extension AirbnbOccupantFilter: AirbnbOccupantFilterControllerDelegate { 77 | func occupantFilterController(_ occupantFilterController: AirbnbOccupantFilterController, didSaveAdult adult: Int, children: Int, infant: Int, pet: Bool) { 78 | 79 | self.adultCount = adult 80 | self.childrenCount = children 81 | self.infantCount = infant 82 | self.hasPet = pet 83 | 84 | let human = adult + children 85 | let infant = "\(infant > 0 ? (infant.description + " infant" + (infant > 1 ? "s" : "")) : "")" 86 | let pet = "\(pet ? "pets" : "")" 87 | 88 | let text = "\(human) guest\(human > 1 ? "s" : "")" + (infant != "" ? ", " + infant : "") + (pet != "" ? ", " + pet : "") 89 | occupantInputButton.setTitle(text, for: .normal) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 4/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-25.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-50.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-75.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Checkmark-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Checkmark-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Checkmark-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Delete-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Delete-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Delete-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-25.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-50.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-75.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Family Man Woman-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Family Man Woman-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Family Man Woman-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-25.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-50.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-75.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Minus-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Minus-48.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Minus-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-25.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-48.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-75.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Plus Math-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Plus Math-48.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Plus Math-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-25.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-48.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-75.png -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/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 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theme.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 4/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Theme { 12 | 13 | static let PRIMARY_COLOR = UIColor(r: 0, g: 132, b: 137) 14 | static let SECONDARY_COLOR = UIColor(r: 65, g: 171, b: 175) 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 4/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | convenience init(hex: String) { 13 | var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() 14 | if (cString.hasPrefix("#")) { 15 | cString = cString.substring(from: cString.index(cString.startIndex, offsetBy: 1)) 16 | } 17 | 18 | if ((cString.characters.count) != 6) { 19 | self.init() 20 | } else { 21 | var rgbValue:UInt32 = 0 22 | Scanner(string: cString).scanHexInt32(&rgbValue) 23 | 24 | self.init( 25 | red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, 26 | green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, 27 | blue: CGFloat(rgbValue & 0x0000FF) / 255.0, 28 | alpha: CGFloat(1.0) 29 | ) 30 | } 31 | } 32 | 33 | convenience init(r: CGFloat, g: CGFloat, b: CGFloat) { 34 | self.init(red: CGFloat(r / 255), green: CGFloat(g / 255), blue: CGFloat(b / 255), alpha: CGFloat(1)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/UIImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 5/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImage { 12 | func tint(with color: UIColor) -> UIImage { 13 | var image = withRenderingMode(.alwaysTemplate) 14 | UIGraphicsBeginImageContextWithOptions(size, false, scale) 15 | color.set() 16 | 17 | image.draw(in: CGRect(origin: .zero, size: size)) 18 | image = UIGraphicsGetImageFromCurrentImageContext()! 19 | UIGraphicsEndImageContext() 20 | return image 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /airbnb-occupant-filter/airbnb-occupant-filter/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 4/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | lazy var occupantFilter: AirbnbOccupantFilter = { 14 | let btn = AirbnbOccupantFilter() 15 | btn.translatesAutoresizingMaskIntoConstraints = false 16 | btn.delegate = self 17 | return btn 18 | }() 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | view.addSubview(occupantFilter) 24 | 25 | occupantFilter.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 26 | occupantFilter.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 27 | occupantFilter.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -40).isActive = true 28 | occupantFilter.heightAnchor.constraint(equalToConstant: 50).isActive = true 29 | 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/AirbnbDatePicker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePicker.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 22/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class AirbnbDatePicker: UIView, AirbnbDatePickerDelegate { 12 | 13 | public var selectedStartDate: Date? 14 | public var selectedEndDate: Date? 15 | public var delegate: UIViewController? 16 | 17 | var dateFormatter: DateFormatter { 18 | get { 19 | let f = DateFormatter() 20 | f.dateFormat = "d MMM" 21 | return f 22 | } 23 | } 24 | 25 | public override init(frame: CGRect) { 26 | super.init(frame: frame) 27 | setupViews() 28 | } 29 | 30 | public required init?(coder aDecoder: NSCoder) { 31 | fatalError("init(coder:) has not been implemented") 32 | } 33 | 34 | public lazy var dateInputButton: UIButton = { 35 | let btn = UIButton() 36 | btn.translatesAutoresizingMaskIntoConstraints = false 37 | btn.backgroundColor = Theme.PRIMARY_COLOR 38 | btn.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left 39 | btn.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) 40 | btn.adjustsImageWhenHighlighted = false 41 | btn.addTarget(self, action: #selector(AirbnbDatePicker.showDatePicker), for: .touchUpInside) 42 | 43 | let img = UIImage(named: "Calendar", in: Bundle(for: AirbnbDatePicker.self), compatibleWith: nil) 44 | btn.setImage(img, for: .normal) 45 | btn.imageView?.contentMode = .scaleAspectFit 46 | 47 | btn.setTitle("Anytime", for: .normal) 48 | btn.setTitleColor(UIColor.white, for: .normal) 49 | btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 15) 50 | btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0) 51 | btn.titleLabel?.lineBreakMode = .byTruncatingTail 52 | 53 | return btn 54 | }() 55 | 56 | func setupViews() { 57 | addSubview(dateInputButton) 58 | 59 | dateInputButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 60 | dateInputButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 61 | dateInputButton.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 62 | dateInputButton.heightAnchor.constraint(equalTo: heightAnchor).isActive = true 63 | } 64 | 65 | @objc func showDatePicker() { 66 | let datePickerViewController = AirbnbDatePickerViewController(dateFrom: selectedStartDate, dateTo: selectedEndDate) 67 | datePickerViewController.delegate = self 68 | let navigationController = UINavigationController(rootViewController: datePickerViewController) 69 | delegate?.present(navigationController, animated: true, completion: nil) 70 | } 71 | 72 | public func datePickerController(_ datePickerController: AirbnbDatePickerViewController, didSaveStartDate startDate: Date?, endDate: Date?) { 73 | selectedStartDate = startDate 74 | selectedEndDate = endDate 75 | 76 | if selectedStartDate == nil && selectedEndDate == nil { 77 | dateInputButton.setTitle("Anytime", for: .normal) 78 | 79 | } else { 80 | dateInputButton.setTitle("\(dateFormatter.string(from: startDate!)) - \(dateFormatter.string(from: endDate!))", for: .normal) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/AirbnbDatePickerCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePickerCell.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 23/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbDatePickerCell: BaseCell { 12 | 13 | var type: AirbnbDatePickerCellType! = [] 14 | 15 | var dateLabel: UILabel = { 16 | let label = UILabel() 17 | label.translatesAutoresizingMaskIntoConstraints = false 18 | label.textAlignment = .center 19 | label.font = UIFont.boldSystemFont(ofSize: 15) 20 | label.textColor = UIColor.white 21 | return label 22 | }() 23 | 24 | var highlightView: UIView = { 25 | let view = UIView() 26 | view.translatesAutoresizingMaskIntoConstraints = false 27 | view.backgroundColor = UIColor.clear 28 | return view 29 | }() 30 | 31 | override func setupViews() { 32 | super.setupViews() 33 | 34 | addSubview(dateLabel) 35 | 36 | dateLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 37 | dateLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 38 | dateLabel.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 39 | dateLabel.heightAnchor.constraint(equalTo: heightAnchor).isActive = true 40 | 41 | addSubview(highlightView) 42 | 43 | highlightView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 44 | highlightView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 45 | highlightView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 46 | highlightView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true 47 | } 48 | 49 | func configureCell() { 50 | if type.contains(.Selected) || type.contains(.SelectedStartDate) || type.contains(.SelectedEndDate) || type.contains(.InBetweenDate) { 51 | 52 | dateLabel.layer.cornerRadius = 0 53 | dateLabel.layer.borderColor = UIColor.white.cgColor 54 | dateLabel.layer.borderWidth = 1 55 | dateLabel.layer.backgroundColor = UIColor.white.cgColor 56 | dateLabel.layer.mask = nil 57 | dateLabel.textColor = Theme.SECONDARY_COLOR 58 | 59 | if type.contains(.SelectedStartDate) { 60 | let side = frame.size.width / 2 61 | let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .bottomLeft], cornerRadii: CGSize(width: side, height: side)) 62 | let shape = CAShapeLayer() 63 | shape.path = maskPath.cgPath 64 | dateLabel.layer.mask = shape 65 | 66 | } else if type.contains(.SelectedEndDate) { 67 | let side = frame.size.width / 2 68 | let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topRight, .bottomRight], cornerRadii: CGSize(width: side, height: side)) 69 | let shape = CAShapeLayer() 70 | shape.path = maskPath.cgPath 71 | dateLabel.layer.mask = shape 72 | 73 | } else if !type.contains(.InBetweenDate) { 74 | dateLabel.layer.cornerRadius = frame.size.width / 2 75 | } 76 | 77 | } else if type.contains(.PastDate) { 78 | 79 | dateLabel.layer.cornerRadius = 0 80 | dateLabel.layer.borderColor = UIColor.clear.cgColor 81 | dateLabel.layer.borderWidth = 0 82 | dateLabel.layer.backgroundColor = UIColor.clear.cgColor 83 | dateLabel.layer.mask = nil 84 | dateLabel.textColor = Theme.SECONDARY_COLOR 85 | 86 | } else if type.contains(.Today) { 87 | 88 | dateLabel.layer.cornerRadius = self.frame.size.width / 2 89 | dateLabel.layer.borderColor = Theme.SECONDARY_COLOR.cgColor 90 | dateLabel.layer.borderWidth = 1 91 | dateLabel.layer.backgroundColor = UIColor.clear.cgColor 92 | dateLabel.layer.mask = nil 93 | dateLabel.textColor = UIColor.white 94 | 95 | } else { 96 | 97 | dateLabel.layer.cornerRadius = 0 98 | dateLabel.layer.borderColor = UIColor.clear.cgColor 99 | dateLabel.layer.borderWidth = 0 100 | dateLabel.layer.backgroundColor = UIColor.clear.cgColor 101 | dateLabel.layer.mask = nil 102 | dateLabel.textColor = UIColor.white 103 | 104 | } 105 | 106 | 107 | if type.contains(.Highlighted) { 108 | highlightView.backgroundColor = UIColor(red: 200 / 255, green: 200 / 255, blue: 200 / 255, alpha: 0.6) 109 | highlightView.layer.cornerRadius = frame.size.width / 2 110 | } else { 111 | highlightView.backgroundColor = UIColor.clear 112 | } 113 | } 114 | } 115 | 116 | struct AirbnbDatePickerCellType: OptionSet { 117 | let rawValue: Int 118 | 119 | static let Date = AirbnbDatePickerCellType(rawValue: 1 << 0) // has number 120 | static let Empty = AirbnbDatePickerCellType(rawValue: 1 << 1) // has no number 121 | static let PastDate = AirbnbDatePickerCellType(rawValue: 1 << 2) // disabled 122 | static let Today = AirbnbDatePickerCellType(rawValue: 1 << 3) // has circle 123 | static let Selected = AirbnbDatePickerCellType(rawValue: 1 << 4) // has filled circle 124 | static let SelectedStartDate = AirbnbDatePickerCellType(rawValue: 1 << 5) // has half filled circle on the left 125 | static let SelectedEndDate = AirbnbDatePickerCellType(rawValue: 1 << 6) // has half filled circle on the right 126 | static let InBetweenDate = AirbnbDatePickerCellType(rawValue: 1 << 7) // has filled square 127 | static let Highlighted = AirbnbDatePickerCellType(rawValue: 1 << 8) // has 128 | } 129 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/AirbnbDatePickerFlowLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePickerFlowLayout.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 28/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbDatePickerFlowLayout : UICollectionViewFlowLayout { 12 | 13 | let cellSpacing:CGFloat = 0 14 | 15 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 16 | if let attributes = super.layoutAttributesForElements(in: rect) { 17 | for (index, attribute) in attributes.enumerated() { 18 | if index == 0 { continue } 19 | let prevLayoutAttributes = attributes[index - 1] 20 | let origin = prevLayoutAttributes.frame.maxX 21 | if origin + cellSpacing + attribute.frame.size.width < self.collectionViewContentSize.width { 22 | attribute.frame.origin.x = origin + cellSpacing 23 | } 24 | } 25 | return attributes 26 | } 27 | return nil 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/AirbnbDatePickerFooter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePickerFooter.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 24/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol AirbnbDatePickerFooterDelegate { 12 | func didSave() 13 | } 14 | 15 | class AirbnbDatePickerFooter: UIView { 16 | 17 | var delegate: AirbnbDatePickerFooterDelegate? 18 | var isSaveEnabled: Bool? { 19 | didSet { 20 | if let enabled = isSaveEnabled, enabled { 21 | saveButton.isEnabled = true 22 | saveButton.alpha = 1 23 | } else { 24 | saveButton.isEnabled = false 25 | saveButton.alpha = 0.5 26 | } 27 | } 28 | } 29 | 30 | lazy var saveButton: UIButton = { 31 | let btn = UIButton() 32 | btn.translatesAutoresizingMaskIntoConstraints = false 33 | btn.backgroundColor = Theme.SECONDARY_COLOR 34 | btn.setTitleColor(UIColor.white, for: .normal) 35 | btn.setTitle("Save", for: .normal) 36 | btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14) 37 | btn.addTarget(self, action: #selector(AirbnbDatePickerFooter.handleSave), for: .touchUpInside) 38 | return btn 39 | }() 40 | 41 | override init(frame: CGRect) { 42 | super.init(frame: frame) 43 | 44 | self.backgroundColor = Theme.PRIMARY_COLOR 45 | 46 | addSubview(saveButton) 47 | 48 | saveButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 49 | saveButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 50 | saveButton.heightAnchor.constraint(equalTo: heightAnchor, constant: -20).isActive = true 51 | saveButton.widthAnchor.constraint(equalTo: widthAnchor, constant: -30).isActive = true 52 | } 53 | 54 | required init?(coder aDecoder: NSCoder) { 55 | fatalError("init(coder:) has not been implemented") 56 | } 57 | 58 | @objc func handleSave() { 59 | if let del = delegate { 60 | del.didSave() 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/AirbnbDatePickerMonthHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbDatePickerMonthHeader.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 23/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbDatePickerMonthHeader: BaseCell { 12 | 13 | var monthLabel: UILabel = { 14 | let label = UILabel() 15 | label.translatesAutoresizingMaskIntoConstraints = false 16 | label.textColor = UIColor.white 17 | label.font = UIFont.systemFont(ofSize: 24) 18 | return label 19 | }() 20 | 21 | override func setupViews() { 22 | super.setupViews() 23 | 24 | addSubview(monthLabel) 25 | 26 | monthLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 20).isActive = true 27 | monthLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true 28 | monthLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 29 | monthLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Calendar-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Calendar-25.png -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Calendar-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Calendar-50.png -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Calendar-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Calendar-75.png -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Calendar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Calendar-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Calendar-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Calendar-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "Delete-75 (1).png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Delete.imageset/Delete-75 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-datepicker/airbnb-datepicker/Assets.xcassets/Delete.imageset/Delete-75 (1).png -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/BaseCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseCell.swift 3 | // padstaview 4 | // 5 | // Created by Yonas Stephen on 11/10/16. 6 | // Copyright © 2016 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BaseCell: UICollectionViewCell { 12 | override init(frame: CGRect) { 13 | super.init(frame: frame) 14 | setupViews() 15 | } 16 | 17 | required init?(coder aDecoder: NSCoder) { 18 | fatalError("init(coder:) has not been implemented") 19 | } 20 | 21 | func setupViews() { 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Date.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 24/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Date { 12 | func startOfMonth() -> Date { 13 | return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self)))! 14 | } 15 | 16 | func endOfMonth() -> Date { 17 | return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())! 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/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 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theme.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 23/2/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Theme { 12 | 13 | static let PRIMARY_COLOR = UIColor(r: 0, g: 132, b: 137) 14 | static let SECONDARY_COLOR = UIColor(r: 65, g: 171, b: 175) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // mychurch-app 4 | // 5 | // Created by Yonas Stephen on 30/6/16. 6 | // Copyright © 2016 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | convenience init(hex: String) { 13 | var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() 14 | if (cString.hasPrefix("#")) { 15 | cString = String(cString.suffix(from: cString.startIndex)) 16 | } 17 | 18 | if ((cString.count) != 6) { 19 | self.init() 20 | } else { 21 | var rgbValue:UInt32 = 0 22 | Scanner(string: cString).scanHexInt32(&rgbValue) 23 | 24 | self.init( 25 | red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, 26 | green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, 27 | blue: CGFloat(rgbValue & 0x0000FF) / 255.0, 28 | alpha: CGFloat(1.0) 29 | ) 30 | } 31 | } 32 | 33 | convenience init(r: CGFloat, g: CGFloat, b: CGFloat) { 34 | self.init(red: CGFloat(r / 255), green: CGFloat(g / 255), blue: CGFloat(b / 255), alpha: CGFloat(1)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/Utility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utility.swift 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 2/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Utility { 12 | 13 | static var calendar: Calendar { 14 | get { 15 | var c = Calendar.current 16 | c.timeZone = TimeZone(abbreviation: "GMT")! 17 | return c 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /frameworks/airbnb-datepicker/airbnb-datepicker/airbnb-datepicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // airbnb-datepicker.h 3 | // airbnb-datepicker 4 | // 5 | // Created by Yonas Stephen on 5/3/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for airbnb-datepicker. 12 | FOUNDATION_EXPORT double airbnb_datepickerVersionNumber; 13 | 14 | //! Project version string for airbnb-datepicker. 15 | FOUNDATION_EXPORT const unsigned char airbnb_datepickerVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/AirbnbOccupantFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbOccupantFilter.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 4/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class AirbnbOccupantFilter: UIView { 12 | 13 | public var delegate: UIViewController? 14 | 15 | var adultCount: Int = 1 16 | var childrenCount: Int = 0 17 | var infantCount: Int = 0 18 | var hasPet: Bool = false 19 | 20 | public var occupantInputButton: UIButton = { 21 | let btn = UIButton() 22 | btn.translatesAutoresizingMaskIntoConstraints = false 23 | btn.backgroundColor = Theme.PRIMARY_COLOR 24 | btn.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left 25 | btn.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) 26 | btn.adjustsImageWhenHighlighted = false 27 | btn.addTarget(self, action: #selector(AirbnbOccupantFilter.showOccupantFilter), for: .touchUpInside) 28 | 29 | let img = UIImage(named: "Family", in: Bundle(for: AirbnbOccupantFilter.self), compatibleWith: nil) 30 | btn.setImage(img, for: .normal) 31 | btn.imageView?.contentMode = .scaleAspectFit 32 | 33 | btn.setTitle("1 guest", for: .normal) 34 | btn.setTitleColor(UIColor.white, for: .normal) 35 | btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 15) 36 | btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0) 37 | btn.titleLabel?.lineBreakMode = .byTruncatingTail 38 | return btn 39 | }() 40 | 41 | public override init(frame: CGRect) { 42 | super.init(frame: frame) 43 | setupViews() 44 | } 45 | 46 | public required init?(coder aDecoder: NSCoder) { 47 | fatalError("init(coder:) has not been implemented") 48 | } 49 | 50 | func setupViews() { 51 | addSubview(occupantInputButton) 52 | 53 | occupantInputButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 54 | occupantInputButton.widthAnchor.constraint(equalTo: widthAnchor).isActive = true 55 | occupantInputButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 56 | occupantInputButton.heightAnchor.constraint(equalTo: heightAnchor).isActive = true 57 | 58 | } 59 | 60 | @objc func showOccupantFilter() { 61 | let occupantController = AirbnbOccupantFilterController(adultCount: adultCount, childrenCount: childrenCount, infantCount: infantCount, hasPet: hasPet) 62 | occupantController.delegate = self 63 | let navigationController = UINavigationController(rootViewController: occupantController) 64 | delegate?.present(navigationController, animated: true, completion: nil) 65 | } 66 | } 67 | 68 | extension AirbnbOccupantFilter: AirbnbOccupantFilterControllerDelegate { 69 | func occupantFilterController(_ occupantFilterController: AirbnbOccupantFilterController, didSaveAdult adult: Int, children: Int, infant: Int, pet: Bool) { 70 | 71 | self.adultCount = adult 72 | self.childrenCount = children 73 | self.infantCount = infant 74 | self.hasPet = pet 75 | 76 | let human = adult + children 77 | let infant = "\(infant > 0 ? (infant.description + " infant" + (infant > 1 ? "s" : "")) : "")" 78 | let pet = "\(pet ? "pets" : "")" 79 | 80 | let text = "\(human) guest\(human > 1 ? "s" : "")" + (infant != "" ? ", " + infant : "") + (pet != "" ? ", " + pet : "") 81 | occupantInputButton.setTitle(text, for: .normal) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/AirbnbSwitch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirbnbSwitch.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 5/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AirbnbSwitch: UIView { 12 | 13 | var isAnimating: Bool = false 14 | var state: Bool = false { 15 | didSet { 16 | toggleButton.isEnabled = false 17 | layoutIfNeeded() 18 | UIView.setAnimationCurve(.easeInOut) 19 | UIView.animate(withDuration: 0.2, animations: { 20 | self.toggleButtonLeftConstraint?.isActive = !self.state 21 | self.toggleButtonRightConstraint?.isActive = self.state 22 | self.toggleButton.setImage(self.state ? self.onImage : self.offImage, for: .normal) 23 | 24 | self.layoutIfNeeded() 25 | }, completion: { success in 26 | self.toggleButton.isEnabled = true 27 | }) 28 | } 29 | } 30 | 31 | var caption: String? { 32 | didSet { 33 | captionLabel.text = caption 34 | } 35 | } 36 | 37 | var subCaption: String? { 38 | didSet { 39 | subCaptionLabel.text = subCaption 40 | } 41 | } 42 | 43 | var toggleButtonLeftConstraint: NSLayoutConstraint? 44 | var toggleButtonRightConstraint: NSLayoutConstraint? 45 | 46 | var offImage: UIImage = { 47 | var img = UIImage(named: "Delete", in: Bundle(for: AirbnbOccupantFilter.self), compatibleWith: nil) 48 | img = img?.tint(with: Theme.PRIMARY_COLOR) 49 | return img! 50 | }() 51 | 52 | var onImage: UIImage = { 53 | var img = UIImage(named: "Checkmark", in: Bundle(for: AirbnbOccupantFilter.self), compatibleWith: nil) 54 | img = img?.tint(with: Theme.PRIMARY_COLOR) 55 | return img! 56 | }() 57 | 58 | var switchContainer: UIView = { 59 | let view = UIView() 60 | view.translatesAutoresizingMaskIntoConstraints = false 61 | 62 | view.layer.borderColor = UIColor.white.cgColor 63 | view.layer.borderWidth = 1 64 | view.layer.cornerRadius = 15 65 | return view 66 | }() 67 | 68 | lazy var toggleButton: UIButton = { 69 | let button = UIButton() 70 | button.translatesAutoresizingMaskIntoConstraints = false 71 | button.setImage(self.offImage, for: .normal) 72 | button.imageEdgeInsets = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) 73 | button.backgroundColor = UIColor.white 74 | button.addTarget(self, action: #selector(AirbnbSwitch.toggle), for: .touchUpInside) 75 | button.adjustsImageWhenHighlighted = false 76 | 77 | button.layer.borderColor = UIColor.white.cgColor 78 | button.layer.borderWidth = 1 79 | button.layer.cornerRadius = 15 80 | return button 81 | }() 82 | 83 | var captionLabel: UILabel = { 84 | let view = UILabel() 85 | view.translatesAutoresizingMaskIntoConstraints = false 86 | view.font = UIFont.boldSystemFont(ofSize: 22) 87 | view.textColor = UIColor.white 88 | return view 89 | }() 90 | 91 | var subCaptionLabel: UILabel = { 92 | let view = UILabel() 93 | view.translatesAutoresizingMaskIntoConstraints = false 94 | view.font = UIFont.systemFont(ofSize: 15) 95 | view.textColor = UIColor.white 96 | return view 97 | }() 98 | 99 | override init(frame: CGRect) { 100 | super.init(frame: frame) 101 | 102 | setupViews() 103 | } 104 | 105 | override func layoutSubviews() { 106 | super.layoutSubviews() 107 | 108 | } 109 | 110 | required init?(coder aDecoder: NSCoder) { 111 | fatalError("init(coder:) has not been implemented") 112 | } 113 | 114 | func setupViews() { 115 | addSubview(switchContainer) 116 | 117 | switchContainer.rightAnchor.constraint(equalTo: rightAnchor).isActive = true 118 | switchContainer.widthAnchor.constraint(equalToConstant: 40).isActive = true 119 | switchContainer.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 120 | switchContainer.heightAnchor.constraint(equalToConstant: 30).isActive = true 121 | 122 | switchContainer.addSubview(toggleButton) 123 | 124 | toggleButtonLeftConstraint = toggleButton.leftAnchor.constraint(equalTo: switchContainer.leftAnchor) 125 | toggleButtonLeftConstraint?.isActive = true 126 | toggleButtonRightConstraint = toggleButton.rightAnchor.constraint(equalTo: switchContainer.rightAnchor) 127 | toggleButton.widthAnchor.constraint(equalToConstant: 30).isActive = true 128 | toggleButton.centerYAnchor.constraint(equalTo: switchContainer.centerYAnchor).isActive = true 129 | toggleButton.heightAnchor.constraint(equalToConstant: 30).isActive = true 130 | 131 | addSubview(captionLabel) 132 | 133 | captionLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true 134 | captionLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 2/3).isActive = true 135 | captionLabel.rightAnchor.constraint(equalTo: switchContainer.leftAnchor).isActive = true 136 | captionLabel.leftAnchor.constraint(equalTo: leftAnchor).isActive = true 137 | 138 | addSubview(subCaptionLabel) 139 | 140 | subCaptionLabel.topAnchor.constraint(equalTo: captionLabel.bottomAnchor).isActive = true 141 | subCaptionLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 1/3).isActive = true 142 | subCaptionLabel.rightAnchor.constraint(equalTo: switchContainer.leftAnchor).isActive = true 143 | subCaptionLabel.leftAnchor.constraint(equalTo: leftAnchor).isActive = true 144 | } 145 | 146 | @objc func toggle() { 147 | state = !state 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-25.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-50.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Checkmark-75.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Checkmark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Checkmark-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Checkmark-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Checkmark-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Delete-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Delete-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Delete-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-25.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-50.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Delete.imageset/Delete-75.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Family Man Woman-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Family Man Woman-50.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Family Man Woman-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-25.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-50.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Family.imageset/Family Man Woman-75.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Minus-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Minus-48.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Minus-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-25.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-48.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Minus.imageset/Minus-75.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Plus Math-25.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Plus Math-48.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Plus Math-75.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-25.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-48.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yonasstephen/swift-of-airbnb/4d4be9428a7257250c97bf06c706590cd2c924f2/frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Assets.xcassets/Plus.imageset/Plus Math-75.png -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/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 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theme.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 4/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Theme { 12 | 13 | static let PRIMARY_COLOR = UIColor(r: 0, g: 132, b: 137) 14 | static let SECONDARY_COLOR = UIColor(r: 65, g: 171, b: 175) 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 4/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | convenience init(hex: String) { 13 | var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() 14 | if (cString.hasPrefix("#")) { 15 | cString = String(cString.suffix(from: cString.startIndex)) 16 | } 17 | 18 | if ((cString.count) != 6) { 19 | self.init() 20 | } else { 21 | var rgbValue:UInt32 = 0 22 | Scanner(string: cString).scanHexInt32(&rgbValue) 23 | 24 | self.init( 25 | red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, 26 | green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, 27 | blue: CGFloat(rgbValue & 0x0000FF) / 255.0, 28 | alpha: CGFloat(1.0) 29 | ) 30 | } 31 | } 32 | 33 | convenience init(r: CGFloat, g: CGFloat, b: CGFloat) { 34 | self.init(red: CGFloat(r / 255), green: CGFloat(g / 255), blue: CGFloat(b / 255), alpha: CGFloat(1)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/UIImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage.swift 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 5/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImage { 12 | func tint(with color: UIColor) -> UIImage { 13 | var image = withRenderingMode(.alwaysTemplate) 14 | UIGraphicsBeginImageContextWithOptions(size, false, scale) 15 | color.set() 16 | 17 | image.draw(in: CGRect(origin: .zero, size: size)) 18 | image = UIGraphicsGetImageFromCurrentImageContext()! 19 | UIGraphicsEndImageContext() 20 | return image 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frameworks/airbnb-occupant-filter/airbnb-occupant-filter/airbnb-occupant-filter.h: -------------------------------------------------------------------------------- 1 | // 2 | // airbnb-occupant-filter.h 3 | // airbnb-occupant-filter 4 | // 5 | // Created by Yonas Stephen on 6/4/17. 6 | // Copyright © 2017 Yonas Stephen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for airbnb-occupant-filter. 12 | FOUNDATION_EXPORT double airbnb_occupant_filterVersionNumber; 13 | 14 | //! Project version string for airbnb-occupant-filter. 15 | FOUNDATION_EXPORT const unsigned char airbnb_occupant_filterVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Swift of Airbnb 5 | 6 | This is a self-taught project of learning Swift inspired by [Sam Lu's 100 Days of Swift] and [Allen Wong's 30 Days of Swift]. Trying to take the learning progress further by making some of Airbnb's screens, which in my opinion is one of the most beautifully-designed iOS App. Kudos to Airbnb for setting such high standard in iOS Development. 7 | 8 | # Projects 9 | 10 | ### Project 1 - Occupant Filter 11 | 12 | ![occupant-filter](https://media.giphy.com/media/l4FGlDyEy1qJu8Fyw/giphy.gif) 13 | 14 | Things I learn: 15 | - Custom counter control AirbnbCounter 16 | - Custom UISwitch control AirbnbSwitch which is basically an image UIButton that animates from left to right when the value toggled 17 | 18 | ### Project 2 - Date Picker 19 | 20 | ![datepicker](https://media.giphy.com/media/3ohzdZ283FnZaCVG12/giphy.gif) 21 | 22 | Things I learn: 23 | - All kinds of things with UICollectionView (didSelect, didHighlight, shouldSelect, shouldHighlight, de/selecting cells programatically, etc.) 24 | - One issue that made me stuck for days was there was tiny pixel gap between date cells that I couldn't remove. I posted the solution here: http://stackoverflow.com/a/42574952/2105910 25 | 26 | ### Project 3 - Main Screen 27 | 28 | ![main screen](https://media.giphy.com/media/xUPGclujPdwArPBpsI/giphy.gif) 29 | 30 | Things I learn: 31 | - How to make iOS Frameworks (using the previous 2 components in this screen) 32 | - UITableView header animation (view position & alpha change as user scrolls) 33 | - Custom page tab navigation with underline border that animates to selected page (For You, Homes, Experience, Places) 34 | - Nested UICollectionView inside UITableView 35 | 36 | ### Project 4 - Custom Page Transition 37 | 38 | ![page transition](https://media.giphy.com/media/xUPGcIN2PQiHGgxv4k/giphy.gif) 39 | 40 | Things I learn: 41 | - A lot from here: https://www.raywenderlich.com/110536/custom-uiviewcontroller-transitions and here: http://blog.matthewcheok.com/design-teardown-preview-expanding-cells/ 42 | - How to slice and snapshot views to animate transition on present and dismiss 43 | - New AirbnbReview component 44 | 45 | # Coming Soon 46 | 47 | This is an ongoing project and a lot of things already planned! 48 | 49 | - ~~Page transition animation on selecting home item~~ 50 | - The map view! (that all other booking apps copy :D) 51 | - Home detail screen 52 | - My trips screen 53 | - or you let me know what you wanna see! 54 | 55 | # Resources 56 | [Michigan Labs] 57 | 58 | [Raywenderlich] 59 | 60 | [Pexels] 61 | 62 | [Icons 8] 63 | 64 | [Let's Build That App] 65 | 66 | # License 67 | 68 | Swift of Airbnb is licensed under MIT License 69 | 70 | [//]: # (These are reference links used in the body of this note and get stripped out when the markdown processor does its job. There is no need to format nicely because it shouldn't be seen. Thanks SO - http://stackoverflow.com/questions/4823468/store-comments-in-markdown-syntax) 71 | 72 | [Allen Wong's 30 Days of Swift]: 73 | [Sam Lu's 100 Days of Swift]: 74 | [Michigan Labs]: 75 | [Raywenderlich]: 76 | [Pexels]: 77 | [Icons 8]: 78 | [Let's Build That App]: 79 | 80 | --------------------------------------------------------------------------------