Repository: MosheBerman/Surfboard
Branch: master
Commit: 7455ec4e7ce2
Files: 47
Total size: 29.9 MB
Directory structure:
gitextract_a9p5efbe/
├── .gitignore
├── Icon/
│ └── Icon.psd
├── LICENSE
├── README.md
├── Surfboard/
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Base.lproj/
│ │ └── Main.storyboard
│ ├── Images.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── LaunchImage.launchimage/
│ │ │ └── Contents.json
│ │ └── iTunesArtwork.imageset/
│ │ └── Contents.json
│ ├── Info.plist
│ ├── SRFSurfboard/
│ │ ├── Panel Cells/
│ │ │ ├── Button Extension/
│ │ │ │ ├── UIButton+IndexPath.h
│ │ │ │ └── UIButton+IndexPath.m
│ │ │ └── Default Cell/
│ │ │ ├── SRFSurfboardPanelCell.h
│ │ │ ├── SRFSurfboardPanelCell.m
│ │ │ └── SRFSurfboardPanelCell.xib
│ │ ├── Panel Data Model/
│ │ │ ├── SRFSurfboardPanel.h
│ │ │ ├── SRFSurfboardPanel.m
│ │ │ └── SRFSurfboardPanelType.h
│ │ ├── SRFSurfboard.h
│ │ ├── Surfboard Delegate Protocol/
│ │ │ └── SRFSurfboardDelegate.h
│ │ └── Surfboard View Controller/
│ │ ├── SRFSurfboardViewController.h
│ │ └── SRFSurfboardViewController.m
│ ├── Sample Data/
│ │ ├── Surfboard.xcassets/
│ │ │ ├── check.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── github.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── happy.imageset/
│ │ │ │ ├── Contents.json
│ │ │ │ └── happy@2x.psd
│ │ │ ├── iPhone.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── panels.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── surfbort.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── surfer.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── swipe.imageset/
│ │ │ │ └── Contents.json
│ │ │ └── twitter.imageset/
│ │ │ └── Contents.json
│ │ └── panels.json
│ ├── ViewController.h
│ ├── ViewController.m
│ └── main.m
├── Surfboard.podspec
└── Surfboard.xcodeproj/
├── project.pbxproj
├── project.xcworkspace/
│ ├── contents.xcworkspacedata
│ ├── xcshareddata/
│ │ └── Surfboard.xccheckout
│ └── xcuserdata/
│ └── moshe.xcuserdatad/
│ ├── UserInterfaceState.xcuserstate
│ └── WorkspaceSettings.xcsettings
└── xcuserdata/
└── moshe.xcuserdatad/
├── xcdebugger/
│ └── Breakpoints_v2.xcbkptlist
└── xcschemes/
├── Surfboard.xcscheme
└── xcschememanagement.plist
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control?
#
# Pods/
================================================
FILE: Icon/Icon.psd
================================================
[File too large to display: 29.8 MB]
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2014 Moshe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
Surfboard
========
Surfboard is a delightful onboarding library for iOS.
Screenshots
---


Dependencies
---
Surfboard was developed with Xcode 5 and the iOS 7 SDK. It uses autolayout and `UICollectionViewController`, so although it hasn't been tested on iOS 6, you may entertain yourself by trying to run Surfboard on it.
Installing Surfboard
---
Add the contents of the SRFSurfboard project to your directory, or use CocoaPods:
`pod 'Surfboard'`
Import `SRFSurfboard.h` to get access to all of the great features of Surfboard.
Getting Started
---
When we talk about Surfboards, we talk about a set of panels. Each panel is a screenful of information, including some text, an image or screenshot, and optionally, a button.
Take a look at the Sample Data directory for an idea of what a sample JSON configuration looks like.
Showing a Surfboard
---
You can show a surfboard by using a segue, or by creating a an instance of `SRFSurfboardViewController` and passing it an array of some panels. You can also pass a path to a JSON file, which we'll discuss in just a second.
The two initializers for a surfboard:
// One surfboard, the initWithPanels: way.
SRFSurfboardViewController *surfboard = [[SRFSurfboardViewController alloc] initWithPanels:anArrayOfPanels];
// Another surfboard, the initWithPathToCofiguration way.
SRFSurfboardViewController *surfboard = [[SRFSurfboardViewController alloc] initWithPathToConfiguration:aPathToJSONFile];
Creating Panels
---
Panels can be created programatically, or using a JSON file. The JSON format is simple. There are four keys:
1. **text** The text that appears at the top of the panel.
2. **image** An image to show in the panel. The image is tinted to the `tintColor` of the surfboard.
3. **screen** A screenshot to show in the panel. The screenshot is not tinted.
4. **button** The title of a button to show at the bottom of the panel. This is optional.
Note that either the contents of the "image" or "screen" will be used, but not both. Supplying both results in undefined behavior.
Here's a sample panel:
{
"text" : "Welcome to Surfboard.",
"image" : "swipe"
}
You'd want to add an image to your bundle or asset catelog named "swipe.png" and Surfboard would display it.
License
---
SRFSurfboard is released under the MIT license. See [LICENSE](./LICENSE) for more.
More Open Source
---
If you like SRFSurfboard, you may like some of [my other projects](https://github.com/MosheBerman?tab=repositories) on GitHub.
================================================
FILE: Surfboard/AppDelegate.h
================================================
//
// AppDelegate.h
// Surfboard
//
// Created by Moshe on 8/7/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
================================================
FILE: Surfboard/AppDelegate.m
================================================
//
// AppDelegate.m
// Surfboard
//
// Created by Moshe on 8/7/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// 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.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// 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.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// 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.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end
================================================
FILE: Surfboard/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11524"/>
<capability name="Segues with Peek and Pop" minToolsVersion="7.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFill" image="iTunesArtwork" translatesAutoresizingMaskIntoConstraints="NO" id="co9-tV-mzz">
<rect key="frame" x="123.5" y="269.5" width="128" height="128"/>
<constraints>
<constraint firstAttribute="height" constant="128" id="uwe-b1-uyo"/>
<constraint firstAttribute="width" constant="128" id="wd7-Xg-1vp"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5eC-Fx-t4G">
<rect key="frame" x="60.5" y="412.5" width="256" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="A4A-6K-LM0"/>
<constraint firstAttribute="width" constant="256" id="LFU-jS-H3O"/>
</constraints>
<state key="normal" title="Tap Here To Surf Again">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<segue destination="kC8-es-wGX" kind="modal" identifier="com.mosheberman.surf" modalPresentationStyle="currentContext" id="rxl-ba-p4e">
<segue key="commit" inheritsFrom="parent" id="Wrt-dh-92Q"/>
<segue key="preview" inheritsFrom="commit" id="FS4-iH-iPS"/>
</segue>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.052771134283610563" green="0.0" blue="0.42332987882653061" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="5eC-Fx-t4G" firstAttribute="top" secondItem="co9-tV-mzz" secondAttribute="bottom" constant="15" id="LQg-hX-Dym"/>
<constraint firstAttribute="centerX" secondItem="co9-tV-mzz" secondAttribute="centerX" id="W5e-xP-yFO"/>
<constraint firstAttribute="centerY" secondItem="co9-tV-mzz" secondAttribute="centerY" id="mBa-w0-jhd"/>
<constraint firstItem="5eC-Fx-t4G" firstAttribute="centerX" secondItem="co9-tV-mzz" secondAttribute="centerX" constant="0.5" id="ygj-J2-FlC"/>
</constraints>
</view>
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
<connections>
<outlet property="view" destination="8bC-Xf-vdC" id="Jd4-OD-29b"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="282" y="82"/>
</scene>
<!--Surfboard View Controller-->
<scene sceneID="iCO-fR-pZl">
<objects>
<collectionViewController id="kC8-es-wGX" customClass="SRFSurfboardViewController" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="VKY-kA-lbH" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="777" y="82"/>
</scene>
</scenes>
<resources>
<image name="iTunesArtwork" width="512" height="512"/>
</resources>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4_7.fullscreen"/>
</simulatedMetricsContainer>
<inferredMetricsTieBreakers>
<segue reference="rxl-ba-p4e"/>
</inferredMetricsTieBreakers>
</document>
================================================
FILE: Surfboard/Images.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon29x29@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon40x40@2x-1.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon60x60@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon29x29.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon29x29@2x-1.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon40x40.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon76x76.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon76x76@2x.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Images.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Images.xcassets/LaunchImage.launchimage/Contents.json
================================================
{
"images" : [
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"subtype" : "retina4",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "to-status-bar",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "to-status-bar",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "to-status-bar",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "to-status-bar",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "8.0",
"subtype" : "736h",
"scale" : "3x"
},
{
"orientation" : "landscape",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "8.0",
"subtype" : "736h",
"scale" : "3x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "8.0",
"subtype" : "667h",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "8.0",
"subtype" : "1366h",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"subtype" : "retina4",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Images.xcassets/iTunesArtwork.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "iTunesArtwork.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "iTunesArtwork@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>Main</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
================================================
FILE: Surfboard/SRFSurfboard/Panel Cells/Button Extension/UIButton+IndexPath.h
================================================
//
// UIButton+IndexPath.h
// Surfboard
//
// Created by Moshe on 8/13/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIButton (IndexPath)
/**
* Sets the button's index path.
*
* @param indexPath The index path to set.
*/
- (void)setIndexPath:(NSIndexPath *)indexPath;
/**
* Retrieve the button's indexPath.
*
* @return The indexPath on the button.
*/
- (NSIndexPath *)indexPath;
@end
================================================
FILE: Surfboard/SRFSurfboard/Panel Cells/Button Extension/UIButton+IndexPath.m
================================================
//
// UIButton+IndexPath.m
// Surfboard
//
// Created by Moshe on 8/13/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import "UIButton+IndexPath.h"
#import <objc/runtime.h>
/**
*
*/
static const char *kPropertyKeyIndexPath = "com.mosheberman.indexPath";
/**
*
*/
@implementation UIButton (IndexPath)
/**
* Sets the button's index path.
*
* @param indexPath The index path to set.
*/
- (void)setIndexPath:(NSIndexPath *)indexPath
{
objc_setAssociatedObject(self, kPropertyKeyIndexPath, indexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
/**
* Retrieve the button's indexPath.
*
* @return The indexPath on the button.
*/
- (NSIndexPath *)indexPath
{
return objc_getAssociatedObject(self, kPropertyKeyIndexPath);
}
@end
================================================
FILE: Surfboard/SRFSurfboard/Panel Cells/Default Cell/SRFSurfboardPanelCell.h
================================================
//
// SRFSurfboardPanel.h
// Surfboard
//
// Created by Moshe on 8/12/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "SRFSurfboardPanel.h"
@interface SRFSurfboardPanelCell : UICollectionViewCell
/**
* An outlet for the text view.
*/
@property (weak, nonatomic) IBOutlet UILabel *textView;
/**
* An outlet for the image view.
*/
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
/**
* The button at the bottom of the view.
*/
@property (weak, nonatomic) IBOutlet UIButton *actionButton;
/**
* The panel contents to be displayed.
*/
@property (nonatomic, strong) SRFSurfboardPanel *panel;
@end
================================================
FILE: Surfboard/SRFSurfboard/Panel Cells/Default Cell/SRFSurfboardPanelCell.m
================================================
//
// SRFSurfboardPanel.m
// Surfboard
//
// Created by Moshe on 8/12/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import "SRFSurfboardPanelCell.h"
/**
* A class extension for SRFSurfboardPanelCell
*/
@interface SRFSurfboardPanelCell ()
/**
* The device image.
*/
@property (weak, nonatomic) IBOutlet UIImageView *deviceImage;
@end
@implementation SRFSurfboardPanelCell
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
self = [super awakeAfterUsingCoder:aDecoder];
if (self)
{
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)prepareForReuse
{
// Set the title text.
self.textView.text = self.panel.text;
// Add the image, tinted
self.imageView.image = self.panel.image;
if (self.imageView.image.renderingMode != UIImageRenderingModeAlwaysTemplate)
{
self.imageView.layer.borderColor = [UIColor lightGrayColor].CGColor;
self.imageView.layer.borderWidth = 1.0f;
}
else
{
self.imageView.layer.borderWidth = 0.0;
}
// Apply the title
[self.actionButton setTitle:self.panel.buttonTitle forState:UIControlStateNormal];
// Hide the button on panels with no title.
self.actionButton.hidden = (self.panel.buttonTitle == nil);
}
- (void)setPanel:(SRFSurfboardPanel *)panel
{
_panel = panel;
[self prepareForReuse];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.contentView.frame = self.bounds;
self.actionButton.layer.cornerRadius = 5.0f;
self.actionButton.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2];
self.textView.textColor = self.tintColor;
if(self.panel.showsDevice == YES)
{
self.deviceImage.image = [self.deviceImage.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}
else
{
self.deviceImage.image = nil;
}
[self.actionButton setTitleColor:self.tintColor forState:UIControlStateNormal];
}
@end
================================================
FILE: Surfboard/SRFSurfboard/Panel Cells/Default Cell/SRFSurfboardPanelCell.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11524"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="jFV-8K-XhM" customClass="SRFSurfboardPanelCell">
<rect key="frame" x="0.0" y="0.0" width="320" height="548"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="320" height="548"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFill" image="iPhone" translatesAutoresizingMaskIntoConstraints="NO" id="ePr-PG-0ND">
<rect key="frame" x="60" y="125" width="190" height="364"/>
<constraints>
<constraint firstAttribute="width" secondItem="ePr-PG-0ND" secondAttribute="height" multiplier="25:48" id="Y9u-gb-GrX"/>
</constraints>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="wiU-3x-Gg0">
<rect key="frame" x="89" y="191" width="132" height="232"/>
<constraints>
<constraint firstAttribute="width" secondItem="wiU-3x-Gg0" secondAttribute="height" multiplier="14:24.55" id="Vrc-wk-5ZR"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" reversesTitleShadowWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SQh-bK-ego">
<rect key="frame" x="8" y="494" width="304" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="WL1-fp-DTI"/>
</constraints>
<state key="normal" title="Button">
<color key="titleColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
</button>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="290" translatesAutoresizingMaskIntoConstraints="NO" id="dMc-RP-y9y">
<rect key="frame" x="15" y="20" width="290" height="100"/>
<constraints>
<constraint firstAttribute="height" constant="100" id="3p3-f8-3ND"/>
</constraints>
<fontDescription key="fontDescription" name="AvenirNext-Regular" family="Avenir Next" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</view>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="SQh-bK-ego" firstAttribute="top" secondItem="ePr-PG-0ND" secondAttribute="bottom" constant="5" id="1u3-eT-nET"/>
<constraint firstItem="ePr-PG-0ND" firstAttribute="top" secondItem="dMc-RP-y9y" secondAttribute="bottom" constant="5" id="6NZ-bs-smW"/>
<constraint firstItem="ePr-PG-0ND" firstAttribute="centerX" secondItem="wiU-3x-Gg0" secondAttribute="centerX" id="Dff-tm-B86"/>
<constraint firstItem="SQh-bK-ego" firstAttribute="leading" secondItem="jFV-8K-XhM" secondAttribute="leadingMargin" id="FZZ-Oj-ZA0"/>
<constraint firstAttribute="trailing" secondItem="dMc-RP-y9y" secondAttribute="trailing" constant="15" id="I8x-gF-Rhp"/>
<constraint firstAttribute="centerX" secondItem="dMc-RP-y9y" secondAttribute="centerX" id="U8h-oj-Yp6"/>
<constraint firstAttribute="centerX" secondItem="ePr-PG-0ND" secondAttribute="centerX" constant="5" id="bYt-wx-GtP"/>
<constraint firstItem="ePr-PG-0ND" firstAttribute="centerY" secondItem="wiU-3x-Gg0" secondAttribute="centerY" id="cCQ-0h-T1l"/>
<constraint firstItem="ePr-PG-0ND" firstAttribute="trailing" secondItem="wiU-3x-Gg0" secondAttribute="trailing" constant="29" id="goo-ea-YZi"/>
<constraint firstItem="dMc-RP-y9y" firstAttribute="top" secondItem="jFV-8K-XhM" secondAttribute="top" constant="20" id="j8E-Ub-hD3"/>
<constraint firstItem="dMc-RP-y9y" firstAttribute="leading" secondItem="jFV-8K-XhM" secondAttribute="leading" constant="15" id="qJX-ip-ubb"/>
<constraint firstAttribute="trailingMargin" secondItem="SQh-bK-ego" secondAttribute="trailing" id="wjf-h6-hUH"/>
<constraint firstAttribute="bottom" secondItem="SQh-bK-ego" secondAttribute="bottom" constant="10" id="x73-Na-3Uj"/>
</constraints>
<connections>
<outlet property="actionButton" destination="SQh-bK-ego" id="r4m-oo-SRv"/>
<outlet property="deviceImage" destination="ePr-PG-0ND" id="wVm-qf-TfX"/>
<outlet property="imageView" destination="wiU-3x-Gg0" id="PZ1-eY-aiv"/>
<outlet property="textView" destination="dMc-RP-y9y" id="XfX-kE-qZi"/>
</connections>
<point key="canvasLocation" x="-158" y="437"/>
</collectionViewCell>
</objects>
<resources>
<image name="iPhone" width="200" height="384"/>
</resources>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4_7.fullscreen"/>
</simulatedMetricsContainer>
</document>
================================================
FILE: Surfboard/SRFSurfboard/Panel Data Model/SRFSurfboardPanel.h
================================================
//
// SRFSurfboardPanel.h
// Surfboard
//
// Created by Moshe on 8/12/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SRFSurfboardPanel : NSObject
/**
* The text to display in the panel.
*/
@property (nonatomic, strong) NSString *text;
/**
* An image to load into the panel.
*
* For the default panel, the dimensions of this
* image should be 140 points wide and 250 points
* tall, or a multiple of that ratio.
*/
@property (nonatomic, strong) UIImage *image;
/**
Determines if the panel should show the device.
Set automatically based on the presence of "screen"
in the configuration dictionary.
*/
@property (nonatomic, assign) BOOL showsDevice;
/**
* A title for a button.
*
* If this is set to nil, the panel cell will
* hide its button.
*/
@property (nonatomic, strong) NSString *buttonTitle;
/**
* Creates a panel with the configuration.
*
* @param configuration A dictionary with configuration options.
* @return A pane.
*/
- (instancetype)initWithConfiguration:(NSDictionary *)configuration;
@end
================================================
FILE: Surfboard/SRFSurfboard/Panel Data Model/SRFSurfboardPanel.m
================================================
//
// SRFSurfboardPanel.m
// Surfboard
//
// Created by Moshe on 8/12/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import "SRFSurfboardPanel.h"
@implementation SRFSurfboardPanel
// MARK: - Initializers
/**
* Creates a panel with the configuration.
*
* @param configuration A dictionary with configuration options.
* @return A pane.
*/
- (instancetype)initWithConfiguration:(NSDictionary *)configuration
{
self = [super init];
if (self)
{
_text = configuration[@"text"];
_image = [[UIImage imageNamed:configuration[@"image"]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
BOOL isScreenshot = [configuration.allKeys containsObject:@"screen"];
if (isScreenshot)
{
_image = [UIImage imageNamed:configuration[@"screen"]];
}
if ([configuration.allKeys containsObject:@"show-device"])
{
_showsDevice = [configuration[@"show-device"] boolValue];
}
else
{
_showsDevice = isScreenshot;
}
_buttonTitle = configuration[@"button"];
}
return self;
}
@end
================================================
FILE: Surfboard/SRFSurfboard/Panel Data Model/SRFSurfboardPanelType.h
================================================
//
// SRFSurfboardPanelType.h
// Surfboard
//
// Created by Moshe Berman on 12/11/16.
// Copyright © 2016 Moshe Berman. All rights reserved.
//
#ifndef SRFSurfboardPanelType_h
#define SRFSurfboardPanelType_h
/**
* A panel type enum - for future use.
*/
NS_ENUM(NSInteger, SRFSurfboardPanelType)
{
SRFSurfboardPanelDefault = 0 // A panel with a block of text at the top, an image, and an optional button.
};
#endif /* SRFSurfboardPanelType_h */
================================================
FILE: Surfboard/SRFSurfboard/SRFSurfboard.h
================================================
//
// SRFSurfboard.h
// Surfboard
//
// Created by Moshe on 8/13/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#ifndef Surfboard_SRFSurfboard_h
#define Surfboard_SRFSurfboard_h
/**
* Hey there!
*
* Surfboard is a delightful onboarding library.
*
* Check out the README to see how it works. Or, run the sample app.
*/
#import "SRFSurfboardViewController.h"
#import "SRFSurfboardPanel.h"
#import "SRFSurfboardDelegate.h"
#endif
================================================
FILE: Surfboard/SRFSurfboard/Surfboard Delegate Protocol/SRFSurfboardDelegate.h
================================================
//
// SRFSurfboardDelegate.h
// Surfboard
//
// Created by Moshe on 8/13/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import <Foundation/Foundation.h>
@class SRFSurfboardViewController;
@class SRFSurfboardPanel;
@protocol SRFSurfboardDelegate <NSObject>
@optional
/**
* Called when the user taps a button in a panel.
*
* @param surfboard The surfboard containing the button.
* @param indexPath The index path of the button.
*
*
* @discussion The section is the panel index, the
* row is the button index within the panel.
*
* Both the button and panel index are zero based.
*
* Tapping on the second button in the first panel
* would produce an indexPath where the section is 0
* and the row is 1.
*
* You can use indexPath.section to get the panel
* where the touch originated from.
*
*/
- (void)surfboard:(SRFSurfboardViewController*)surfboard didTapButtonAtIndexPath:(NSIndexPath *)indexPath;
/**
* Called when the surfboard changes to a panel.
*
* @param index The index of the panel.
*/
- (void)surfboard:(SRFSurfboardViewController *)surfboard didShowPanelAtIndex:(NSInteger)index;
@end
================================================
FILE: Surfboard/SRFSurfboard/Surfboard View Controller/SRFSurfboardViewController.h
================================================
//
// MBSurfboardViewController.h
// Surfboard
//
// Created by Moshe on 8/10/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "SRFSurfboardDelegate.h"
@interface SRFSurfboardViewController : UICollectionViewController
#pragma mark - Delegate
/** ---
* @name Delegate
* ---
*/
@property (nonatomic, assign) id<SRFSurfboardDelegate> delegate;
#pragma mark - Colors
/** ---
* @name Color
* ---
*/
/**
* The tint color of the cells.
*/
@property (nonatomic, strong) UIColor *tintColor;
/**
* The background color of the view.
*/
@property (nonatomic, strong) UIColor *backgroundColor;
#pragma mark - Initializers
/**
* Instantiates a new surfboard with an array of panel objects.
*
* @param configuration An array of panels.
* @return An SRFSurfboardViewController.
*/
- (instancetype)initWithPathToConfiguration:(NSString *)path;
/**
* Instantiates a new surfboard with an array of panel objects.
*
* @param panels An array of panels.
* @return An SRFSurfboardViewController
*/
- (instancetype)initWithPanels:(NSArray *)panels NS_DESIGNATED_INITIALIZER;
// MARK: - Setting the Panels After Initializing
/**
* Sets the panels and reloads the surfboard.
*
* @param panels An array of panels.
*/
- (void)setPanels:(NSArray *)panels;
// MARK: - Load Panels from Configuration
/**
* Loads panels out from a given path.
*
* @param path The path from which to load the panel configuration.
*
* @return An array of panels.
*/
+ (NSArray *)panelsFromConfigurationAtPath:(NSString *)path;
// MARK: - Programatic Navigation
/**
Slides to the next panel if there is one.
@param animated Determines if the change should be animated or not.
*/
- (void)navigateToNextPanelAnimated:(BOOL)animated;
/**
Slides to the previous panel if there is one.
@param animated Determines if the change should be animated or not.
*/
- (void)navigateToPreviousPanelAnimated:(BOOL)animated;
@end
================================================
FILE: Surfboard/SRFSurfboard/Surfboard View Controller/SRFSurfboardViewController.m
================================================
//
// MBSurfboardViewController.m
// Surfboard
//
// Created by Moshe on 8/10/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import "SRFSurfboardViewController.h"
#import "SRFSurfboardPanelCell.h" // the panel cell
#import "SRFSurfboardPanel.h"
#import "UIButton+IndexPath.h"
/**
* An identifier for surfboard cells.
*/
static NSString *kSurfboardPanelIdentifier = @"com.mosheberman.surfboard-panel";
/**
* An extension of the surfbord view controller.
*/
@interface SRFSurfboardViewController () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UIScrollViewDelegate>
/**
* An array of configuration elements.
*/
@property (nonatomic, strong) NSArray *panels;
/**
* An index of the current view.
*/
@property (nonatomic, assign) NSInteger index;
/**
* A page control.
*/
@property (nonatomic, strong) UIPageControl *pageControl;
/**
* A flag to control index updates during rotation.
*/
@property (nonatomic, assign) BOOL isRotating;
@end
@implementation SRFSurfboardViewController
// MARK: - Initializers
/**
* Instantiates a new surfboard with an array of panel objects.
*
* @param configuration An array of panels.
* @return An SRFSurfboardViewController.
*/
- (instancetype)initWithPathToConfiguration:(NSString *)path
{
NSArray *panels = [SRFSurfboardViewController panelsFromConfigurationAtPath:path];
self = [self initWithPanels:panels];
if (self)
{
}
return self;
}
/**
* Instantiates a new surfboard with an array of panel objects.
*
* @param panels An array of panels.
* @return An SRFSurfboardViewController.
*/
- (instancetype)initWithPanels:(NSArray *)panels
{
self = [super initWithCollectionViewLayout:[[UICollectionViewFlowLayout alloc] init]];
if (self)
{
_panels = panels;
_pageControl = [[UIPageControl alloc] init];
_pageControl.userInteractionEnabled = NO;
_pageControl.defersCurrentPageDisplay = YES;
_pageControl.numberOfPages = panels.count;
_isRotating = NO;
_tintColor = [UIColor whiteColor];
_backgroundColor = [UIColor blueColor];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [self initWithPanels:nil];
return self;
}
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
{
self = [self initWithPanels:nil];
return self;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
return [self initWithPanels:nil];
}
#pragma mark - View Lifecycle
/** ---
* @name View Lifecycle
* ---
*/
- (void)viewDidLoad
{
[super viewDidLoad];
/**
* Apply the tint color to the cells.
*/
self.tintColor = self.tintColor;
/**
* Apply a background color.
*/
self.collectionView.backgroundColor = self.backgroundColor;
/**
* Configure the layout.
*/
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
/**
* Register a nib for the surfboard panel class.
*/
UINib *nib = [UINib nibWithNibName:@"SRFSurfboardPanelCell" bundle:nil];
[self.collectionView registerNib:nib forCellWithReuseIdentifier:kSurfboardPanelIdentifier];
/**
* Wire up the delegate and data source.
*/
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
/**
* Enable paging.
*/
self.collectionView.pagingEnabled = YES;
self.collectionView.showsHorizontalScrollIndicator = NO;
self.collectionView.showsVerticalScrollIndicator = NO;
/**
* Debug borders Yay!
*/
// self.collectionView.layer.borderColor = [UIColor redColor].CGColor;
// self.collectionView.layer.borderWidth = 2.0f;
}
/**
* Add a page control
*/
- (void)viewDidAppear:(BOOL)animated
{
[self _addPageControl];
[self _hackBorderOnPageControlSubviews];
[self _adjustPageControlVisibilityForPanelAtIndex:self.index];
}
// MARK: - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
NSInteger count = self.panels.count;
return count;
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
SRFSurfboardPanelCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kSurfboardPanelIdentifier forIndexPath:indexPath];
if (indexPath.row >= 0 && indexPath.row < self.panels.count)
{
SRFSurfboardPanel *panel = self.panels[indexPath.row];
cell.panel = panel;
[self _prepareButtonsInCell:cell atIndexPath:indexPath];
[self _adjustPageControlVisibilityForPanelAtIndex:indexPath.row];
}
return cell;
}
// MARK: - Programatic Navigation
/**
Slides to the next panel if there is one.
@param animated Determines if the change should be animated or not.
*/
- (void)navigateToNextPanelAnimated:(BOOL)animated;
{
if (self.index < self.panels.count - 1)
{
[self _navigateToIndex:self.index + 1 animated:animated];
}
}
/**
Slides to the previous panel if there is one.
@param animated Determines if the change should be animated or not.
*/
- (void)navigateToPreviousPanelAnimated:(BOOL)animated;
{
if (self.index > 0 && self.panels.count > 0)
{
[self _navigateToIndex:self.index - 1 animated:animated];
}
}
- (void)_navigateToIndex:(NSInteger)index animated:(BOOL)animated
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:(UICollectionViewScrollPositionCenteredVertically |UICollectionViewScrollPositionCenteredHorizontally) animated:animated];
}
// MARK: - Wire Up the Buttons
- (void)_prepareButtonsInCell:(SRFSurfboardPanelCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
/**
* We need to prepare the buttons in this cell.
*/
SRFSurfboardPanelCell *panelCell = (SRFSurfboardPanelCell *)cell;
/**
* Start with an index of zero.
*/
NSInteger buttonIndex = 0;
if ([panelCell isKindOfClass:[SRFSurfboardPanelCell class]])
{
for (UIView *view in panelCell.contentView.subviews)
{
/**
* For each button, set the tag, then increment.
*/
if ([view isKindOfClass:[UIButton class]])
{
/**
* Grab the button.
*/
UIButton *button = (UIButton *)view;
if([button respondsToSelector:@selector(setIndexPath:)])
{
/**
* The indexPath is added in class extension defined in the UIButton_Surfboard.h header.
* Here we set the indexPath based on the panel and the index.
*/
button.indexPath = [NSIndexPath indexPathForRow:buttonIndex inSection:indexPath.row];
/**
* Increment the buttonIndex.
*/
buttonIndex++;
/**
* Remove any old wiring from the button and wire it up again.
*/
[button removeTarget:nil action:NULL forControlEvents:UIControlEventAllEvents];
[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
else
{
NSLog(@"(SRFSurfboardViewController) : Failed to load UIButton extension. Buttons will not work.");
}
}
}
}
}
#pragma mark - UICollectionViewDelegateFlowLayout
/** ---
* @name UICollectionViewDelegateFlowLayout
* ---
*/
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return self.collectionView.frame.size;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 0.0f;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return 0.0f;
}
#pragma mark - Setters
/** ---
* @name Setters
* ---
*/
/**
* Sets the panels and reloads the surfboard.
*
* @param panels An array of panels.
*/
- (void)setPanels:(NSArray *)panels
{
_panels = panels;
[[self collectionViewLayout] invalidateLayout];
[[self collectionView] reloadData];
}
/**
* Sets the tint color and reloads the collection view.
*
* @param tintColor A tint color to apply to the surfboard.
*/
- (void)setTintColor:(UIColor *)tintColor
{
_tintColor = tintColor;
self.collectionView.tintColor = tintColor;
[self.collectionView reloadData];
}
/**
*
*/
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
self.collectionView.backgroundColor = backgroundColor;
}
#pragma mark - Load Panels from Configuration
/** ---
* @name Load Panels From Configuration
* ---
*/
/**
* Loads panels out from a given path.
*
* @param path The path from which to load the panel configuration.
*
* @return An array of panels.
*/
+ (NSArray *)panelsFromConfigurationAtPath:(NSString *)path
{
NSData *panelData = [[NSData alloc] initWithContentsOfFile:path];
NSMutableArray *panels = [[NSMutableArray alloc] init];
if (panelData)
{
NSArray *panelDictionaries = [NSJSONSerialization JSONObjectWithData:panelData options:0 error:nil];
/**
* Iterate the panel dictionaries and "inflate" them into objects.
*/
for (NSDictionary *panelDictionary in panelDictionaries)
{
SRFSurfboardPanel *panel = [[SRFSurfboardPanel alloc] initWithConfiguration:panelDictionary];
[panels addObject:panel];
}
}
return panels;
}
#pragma mark - Rotation Handling
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
/**
* Enable the rotation flag.
*/
self.isRotating = YES;
// Ensure we have the right index.
[self.collectionViewLayout invalidateLayout];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
/**
* Disable the rotation flag.
*/
self.isRotating = NO;
/**
* Force the layout to redraw, which will resize the cells.
*/
[self.collectionViewLayout invalidateLayout];
/**
* Center the previously centered cell.
*/
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.index inSection:0];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
/**
* Update the page control.
*/
[self _positionPageControl];
[self _adjustPageControlVisibilityForPanelAtIndex:self.index];
}
#pragma mark - UIScrollViewDelegate
/**
* Although we adjust the page control's
* alpha when dequeuing a cell, we need to
* to so again after dragging, just in case
* there's a false start.
*/
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self _adjustPageControlVisibilityForPanelAtIndex:self.index];
if ([self.delegate respondsToSelector:@selector(surfboard:didShowPanelAtIndex:)])
{
[self.delegate surfboard:self didShowPanelAtIndex:self.index];
}
}
/**
* Whenever the collection view scrolls,
* we need to update the page control.
*/
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (self.isRotating == YES)
{
return;
}
self.index = [self _calculatedIndex];
[self _positionPageControl];
[self _selectActivePage];
[self.collectionView sendSubviewToBack:self.pageControl];
}
#pragma mark - Page Control
/**
* This method sets the number of pages,
* resizes the page control, positions
* it, and finally, adds the page control
* to the view hierarchy.
*/
- (void)_addPageControl
{
self.pageControl.numberOfPages = self.panels.count;
[self.pageControl sizeToFit];
self.pageControl.alpha = 0.0f;
[self.collectionView addSubview:self.pageControl];
[self.collectionView sendSubviewToBack:self.pageControl];
[self _positionPageControl];
[UIView animateWithDuration:0.15 animations:^{
self.pageControl.alpha = 1.0f;
}];
}
/**
* This method calculates and sets the
* current page of the page control.
*/
- (void)_selectActivePage
{
self.pageControl.currentPage = self.index;
}
/**
* This method calculate and sets
* the position of the page control.
*/
- (void)_positionPageControl
{
if (![self.collectionView.subviews containsObject:self.pageControl])
{
NSLog(@"(SRFSurfboard) : I can't layout the page control until it's in the view hierarchy.");
return;
}
CGRect visibleRect = [self _visibleRect];
CGFloat pageControlX = CGRectGetMidX(visibleRect);
CGFloat pageControlY = CGRectGetHeight(self.collectionView.bounds) - CGRectGetHeight(self.pageControl.frame);
CGPoint center = CGPointMake(pageControlX, pageControlY);
self.pageControl.center = center;
}
/**
* Hides the page control if we think
* that it will overlap a button.
*/
- (void)_adjustPageControlVisibilityForPanelAtIndex:(NSInteger)index
{
/**
* Get a panel, if we have one at the current index.
*/
SRFSurfboardPanel *panel = nil;
if (index >= 0 || index < self.panels.count)
{
panel = self.panels[index];
}
/**
* If there are one or more button titles,
* we need to hide the page control, so
* it doesn't overlap the cell.
*
* Also, if we didn't get a panel in the
* previous step, we're going to simply
* hide the page control.
*/
if (panel.buttonTitle || panel == nil)
{
[UIView animateWithDuration:0.2 animations:^{
self.pageControl.alpha = 0.0f;
}];
}
/**
* Otherwise, we want to ensure that the
* page control is visible.
*/
else
{
[UIView animateWithDuration:0.2 animations:^{
self.pageControl.alpha = 1.0f;
}];
}
/**
* Add a border to each dot in the page control.
*/
[self _hackBorderOnPageControlSubviews];
}
- (void)_hackBorderOnPageControlSubviews
{
for (UIView *view in self.pageControl.subviews)
{
view.layer.borderColor = [UIColor whiteColor].CGColor;
view.layer.borderWidth = 1.0f;
}
}
#pragma mark - Scrolling Progress
/** ---
* @name Scrolling Progress
* ---
*/
/**
* Calculates the index based on the offset and the visible rect.
*/
- (NSInteger)_calculatedIndex
{
CGFloat width = CGRectGetWidth(self.collectionView.bounds);
CGFloat offset = self.collectionView.contentOffset.x;
CGFloat index = offset/width;
return index;
}
/**
* Calculates the visible rectangle of the collection view.
*/
- (CGRect)_visibleRect
{
CGRect visibleRect = CGRectZero;
visibleRect.size = self.collectionView.bounds.size;
visibleRect.origin = self.collectionView.contentOffset;
return visibleRect;
}
// MARK: - Button Delegate
/**
Called when a button is tapped.
@param button The button that was tapped.
*/
- (void)buttonTapped:(UIButton *)button
{
if ([self.delegate respondsToSelector:@selector(surfboard:didTapButtonAtIndexPath:)])
{
[self.delegate surfboard:self didTapButtonAtIndexPath:button.indexPath];
}
}
@end
================================================
FILE: Surfboard/Sample Data/Surfboard.xcassets/check.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "arrow.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "arrow@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Sample Data/Surfboard.xcassets/github.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "github.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "github@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Sample Data/Surfboard.xcassets/happy.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "happy.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "happy@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Sample Data/Surfboard.xcassets/iPhone.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "iPhone.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "iPhone@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode",
"template-rendering-intent" : "template"
}
}
================================================
FILE: Surfboard/Sample Data/Surfboard.xcassets/panels.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "panels.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "panels@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Sample Data/Surfboard.xcassets/surfbort.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "surfbort.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "surfbort@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "surfbort@3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Sample Data/Surfboard.xcassets/surfer.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "surfer.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "surfer@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Sample Data/Surfboard.xcassets/swipe.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "swipe.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "swipe@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Sample Data/Surfboard.xcassets/twitter.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "twitter.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "twitter@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Surfboard/Sample Data/panels.json
================================================
[
{
"text" : "Welcome to Surfboard.",
"image" : "swipe",
"show-device" : true
},
{
"text" : "Surfboard makes it delightfully easy to craft onboarding experiences.",
"image" : "surfer",
},
{
"text" : "You provide an array of panels and Surfboard figures out the rest.",
"image" : "panels",
"show-device" : true
},
{
"text" : "See the documentation on GitHub for more information.",
"image" : "github",
},
{
"text" : "If you like Surfboard give me a shoutout on Twitter. I'm @bermaniastudios.",
"screen" : "twitter",
"button" : "Thanks for Surfing!"
}
]
================================================
FILE: Surfboard/ViewController.h
================================================
//
// ViewController.h
// Surfboard
//
// Created by Moshe on 8/7/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
================================================
FILE: Surfboard/ViewController.m
================================================
//
// ViewController.m
// Surfboard
//
// Created by Moshe on 8/7/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import "ViewController.h"
#import "SRFSurfboard.h"
@interface ViewController () <SRFSurfboardDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
/**
* Set the background color to look like there's some kind of mist in the icon.
*/
self.view.backgroundColor = [UIColor colorWithRed:0.06 green:0.15 blue:0.63 alpha:1.00];
/**
* Find the buttons in the view and give them some kind of affordance.
*/
for (UIView *view in self.view.subviews)
{
if ([view isKindOfClass:[UIButton class]])
{
view.layer.cornerRadius = 5.0f;
view.layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.25].CGColor;
}
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
return [super shouldPerformSegueWithIdentifier:identifier sender:sender];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SRFSurfboardViewController *surfboard = segue.destinationViewController;
NSString *path = [[NSBundle mainBundle] pathForResource:@"panels" ofType:@"json"];
NSArray *panels = [SRFSurfboardViewController panelsFromConfigurationAtPath:path];
[surfboard setPanels:panels];
surfboard.delegate = self;
surfboard.backgroundColor = self.view.backgroundColor;
}
#pragma mark - SRFSurfboardDelegate
/** ---
* @name SRFSurfboardDelegate
* ---
*/
- (void)surfboard:(SRFSurfboardViewController *)surfboard didTapButtonAtIndexPath:(NSIndexPath *)indexPath
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)surfboard:(SRFSurfboardViewController *)surfboard didShowPanelAtIndex:(NSInteger)index
{
// NSLog(@"Index: %i", index);
}
@end
================================================
FILE: Surfboard/main.m
================================================
//
// main.m
// Surfboard
//
// Created by Moshe on 8/7/14.
// Copyright (c) 2014 Moshe Berman. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
================================================
FILE: Surfboard.podspec
================================================
Pod::Spec.new do |s|
s.name = "Surfboard"
s.version = "1.0.0"
s.summary = "Surfboard is a delightful onboarding library for iOS."
s.description = <<-DESC
Surfboard is a delightful onboarding library for iOS. Configure your onboarding experience with JSON.
DESC
s.homepage = "https://github.com/MosheBerman/Surfboard"
s.screenshots = "https://raw.github.com/MosheBerman/Surfboard/master/Screenshots/Intro.png", "https://raw.github.com/MosheBerman/Surfboard/master/Screenshots/Panels.png"
s.author = { "Moshe Berman" => "moshberm@gmail.com" }
s.license = 'MIT'
s.platform = :ios, '7.0'
s.source = { :git => "https://github.com/MosheBerman/Surfboard.git", :tag => "1.0.0"}
s.source_files = 'Classes', 'Surfboard/SRFSurfboard/**/*.{h,m}'
s.requires_arc = true
end
================================================
FILE: Surfboard.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
462C10861993E21600A36CF8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 462C10851993E21600A36CF8 /* main.m */; };
462C10891993E21600A36CF8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 462C10881993E21600A36CF8 /* AppDelegate.m */; };
462C108C1993E21600A36CF8 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 462C108B1993E21600A36CF8 /* ViewController.m */; };
462C108F1993E21600A36CF8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 462C108D1993E21600A36CF8 /* Main.storyboard */; };
462C10911993E21600A36CF8 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 462C10901993E21600A36CF8 /* Images.xcassets */; };
4635679619A7C5A000262061 /* panels.json in Resources */ = {isa = PBXBuildFile; fileRef = 4635679419A7C5A000262061 /* panels.json */; };
4635679719A7C5A000262061 /* Surfboard.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4635679519A7C5A000262061 /* Surfboard.xcassets */; };
463567AA19A7C65800262061 /* UIButton+IndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 4635679C19A7C65800262061 /* UIButton+IndexPath.m */; };
463567AB19A7C65800262061 /* SRFSurfboardPanelCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4635679F19A7C65800262061 /* SRFSurfboardPanelCell.m */; };
463567AC19A7C65800262061 /* SRFSurfboardPanelCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 463567A019A7C65800262061 /* SRFSurfboardPanelCell.xib */; };
463567AD19A7C65800262061 /* SRFSurfboardPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = 463567A319A7C65800262061 /* SRFSurfboardPanel.m */; };
463567AE19A7C65800262061 /* SRFSurfboardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 463567A919A7C65800262061 /* SRFSurfboardViewController.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
462C10801993E21600A36CF8 /* Surfboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Surfboard.app; sourceTree = BUILT_PRODUCTS_DIR; };
462C10841993E21600A36CF8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
462C10851993E21600A36CF8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
462C10871993E21600A36CF8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
462C10881993E21600A36CF8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
462C108A1993E21600A36CF8 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
462C108B1993E21600A36CF8 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
462C108E1993E21600A36CF8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
462C10901993E21600A36CF8 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
4635679419A7C5A000262061 /* panels.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = panels.json; sourceTree = "<group>"; };
4635679519A7C5A000262061 /* Surfboard.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Surfboard.xcassets; sourceTree = "<group>"; };
4635679B19A7C65800262061 /* UIButton+IndexPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+IndexPath.h"; sourceTree = "<group>"; };
4635679C19A7C65800262061 /* UIButton+IndexPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+IndexPath.m"; sourceTree = "<group>"; };
4635679E19A7C65800262061 /* SRFSurfboardPanelCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRFSurfboardPanelCell.h; sourceTree = "<group>"; };
4635679F19A7C65800262061 /* SRFSurfboardPanelCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRFSurfboardPanelCell.m; sourceTree = "<group>"; };
463567A019A7C65800262061 /* SRFSurfboardPanelCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SRFSurfboardPanelCell.xib; sourceTree = "<group>"; };
463567A219A7C65800262061 /* SRFSurfboardPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRFSurfboardPanel.h; sourceTree = "<group>"; };
463567A319A7C65800262061 /* SRFSurfboardPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRFSurfboardPanel.m; sourceTree = "<group>"; };
463567A419A7C65800262061 /* SRFSurfboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRFSurfboard.h; sourceTree = "<group>"; };
463567A619A7C65800262061 /* SRFSurfboardDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRFSurfboardDelegate.h; sourceTree = "<group>"; };
463567A819A7C65800262061 /* SRFSurfboardViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRFSurfboardViewController.h; sourceTree = "<group>"; };
463567A919A7C65800262061 /* SRFSurfboardViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRFSurfboardViewController.m; sourceTree = "<group>"; };
465138DB1DFDB2FD0002AB36 /* SRFSurfboardPanelType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SRFSurfboardPanelType.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
462C107D1993E21600A36CF8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
462C10771993E21600A36CF8 = {
isa = PBXGroup;
children = (
462C10821993E21600A36CF8 /* Surfboard */,
462C10811993E21600A36CF8 /* Products */,
);
sourceTree = "<group>";
};
462C10811993E21600A36CF8 /* Products */ = {
isa = PBXGroup;
children = (
462C10801993E21600A36CF8 /* Surfboard.app */,
);
name = Products;
sourceTree = "<group>";
};
462C10821993E21600A36CF8 /* Surfboard */ = {
isa = PBXGroup;
children = (
462C10871993E21600A36CF8 /* AppDelegate.h */,
462C10881993E21600A36CF8 /* AppDelegate.m */,
462C108A1993E21600A36CF8 /* ViewController.h */,
462C108B1993E21600A36CF8 /* ViewController.m */,
4635679819A7C65800262061 /* SRFSurfboard */,
4635679319A7C5A000262061 /* Sample Data */,
462C108D1993E21600A36CF8 /* Main.storyboard */,
462C10901993E21600A36CF8 /* Images.xcassets */,
462C10831993E21600A36CF8 /* Supporting Files */,
);
path = Surfboard;
sourceTree = "<group>";
};
462C10831993E21600A36CF8 /* Supporting Files */ = {
isa = PBXGroup;
children = (
462C10841993E21600A36CF8 /* Info.plist */,
462C10851993E21600A36CF8 /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
4635679319A7C5A000262061 /* Sample Data */ = {
isa = PBXGroup;
children = (
4635679419A7C5A000262061 /* panels.json */,
4635679519A7C5A000262061 /* Surfboard.xcassets */,
);
name = "Sample Data";
path = "Surfboard/Sample Data";
sourceTree = SOURCE_ROOT;
};
4635679819A7C65800262061 /* SRFSurfboard */ = {
isa = PBXGroup;
children = (
463567A419A7C65800262061 /* SRFSurfboard.h */,
4635679919A7C65800262061 /* Panel Cells */,
463567A119A7C65800262061 /* Panel Data Model */,
463567A519A7C65800262061 /* Surfboard Delegate Protocol */,
463567A719A7C65800262061 /* Surfboard View Controller */,
);
path = SRFSurfboard;
sourceTree = "<group>";
};
4635679919A7C65800262061 /* Panel Cells */ = {
isa = PBXGroup;
children = (
4635679A19A7C65800262061 /* Button Extension */,
4635679D19A7C65800262061 /* Default Cell */,
);
path = "Panel Cells";
sourceTree = "<group>";
};
4635679A19A7C65800262061 /* Button Extension */ = {
isa = PBXGroup;
children = (
4635679B19A7C65800262061 /* UIButton+IndexPath.h */,
4635679C19A7C65800262061 /* UIButton+IndexPath.m */,
);
path = "Button Extension";
sourceTree = "<group>";
};
4635679D19A7C65800262061 /* Default Cell */ = {
isa = PBXGroup;
children = (
4635679E19A7C65800262061 /* SRFSurfboardPanelCell.h */,
4635679F19A7C65800262061 /* SRFSurfboardPanelCell.m */,
463567A019A7C65800262061 /* SRFSurfboardPanelCell.xib */,
);
path = "Default Cell";
sourceTree = "<group>";
};
463567A119A7C65800262061 /* Panel Data Model */ = {
isa = PBXGroup;
children = (
463567A219A7C65800262061 /* SRFSurfboardPanel.h */,
463567A319A7C65800262061 /* SRFSurfboardPanel.m */,
465138DB1DFDB2FD0002AB36 /* SRFSurfboardPanelType.h */,
);
path = "Panel Data Model";
sourceTree = "<group>";
};
463567A519A7C65800262061 /* Surfboard Delegate Protocol */ = {
isa = PBXGroup;
children = (
463567A619A7C65800262061 /* SRFSurfboardDelegate.h */,
);
path = "Surfboard Delegate Protocol";
sourceTree = "<group>";
};
463567A719A7C65800262061 /* Surfboard View Controller */ = {
isa = PBXGroup;
children = (
463567A819A7C65800262061 /* SRFSurfboardViewController.h */,
463567A919A7C65800262061 /* SRFSurfboardViewController.m */,
);
path = "Surfboard View Controller";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
462C107F1993E21600A36CF8 /* Surfboard */ = {
isa = PBXNativeTarget;
buildConfigurationList = 462C10A01993E21600A36CF8 /* Build configuration list for PBXNativeTarget "Surfboard" */;
buildPhases = (
462C107C1993E21600A36CF8 /* Sources */,
462C107D1993E21600A36CF8 /* Frameworks */,
462C107E1993E21600A36CF8 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = Surfboard;
productName = Surfboard;
productReference = 462C10801993E21600A36CF8 /* Surfboard.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
462C10781993E21600A36CF8 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0810;
ORGANIZATIONNAME = "Moshe Berman";
TargetAttributes = {
462C107F1993E21600A36CF8 = {
CreatedOnToolsVersion = 6.0;
DevelopmentTeam = HK64RUWW3P;
};
};
};
buildConfigurationList = 462C107B1993E21600A36CF8 /* Build configuration list for PBXProject "Surfboard" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 462C10771993E21600A36CF8;
productRefGroup = 462C10811993E21600A36CF8 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
462C107F1993E21600A36CF8 /* Surfboard */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
462C107E1993E21600A36CF8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
462C108F1993E21600A36CF8 /* Main.storyboard in Resources */,
4635679719A7C5A000262061 /* Surfboard.xcassets in Resources */,
463567AC19A7C65800262061 /* SRFSurfboardPanelCell.xib in Resources */,
4635679619A7C5A000262061 /* panels.json in Resources */,
462C10911993E21600A36CF8 /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
462C107C1993E21600A36CF8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
462C108C1993E21600A36CF8 /* ViewController.m in Sources */,
463567AE19A7C65800262061 /* SRFSurfboardViewController.m in Sources */,
463567AA19A7C65800262061 /* UIButton+IndexPath.m in Sources */,
462C10891993E21600A36CF8 /* AppDelegate.m in Sources */,
463567AD19A7C65800262061 /* SRFSurfboardPanel.m in Sources */,
462C10861993E21600A36CF8 /* main.m in Sources */,
463567AB19A7C65800262061 /* SRFSurfboardPanelCell.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
462C108D1993E21600A36CF8 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
462C108E1993E21600A36CF8 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
462C109E1993E21600A36CF8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
462C109F1993E21600A36CF8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
462C10A11993E21600A36CF8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
INFOPLIST_FILE = Surfboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.mosheberman.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
};
name = Debug;
};
462C10A21993E21600A36CF8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
INFOPLIST_FILE = Surfboard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.mosheberman.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
462C107B1993E21600A36CF8 /* Build configuration list for PBXProject "Surfboard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
462C109E1993E21600A36CF8 /* Debug */,
462C109F1993E21600A36CF8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
462C10A01993E21600A36CF8 /* Build configuration list for PBXNativeTarget "Surfboard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
462C10A11993E21600A36CF8 /* Debug */,
462C10A21993E21600A36CF8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 462C10781993E21600A36CF8 /* Project object */;
}
================================================
FILE: Surfboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Surfboard.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: Surfboard.xcodeproj/project.xcworkspace/xcshareddata/Surfboard.xccheckout
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>D5BD5DA1-72E7-4A86-8A92-4FE1C9F1C35F</string>
<key>IDESourceControlProjectName</key>
<string>Surfboard</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>40FF93D2-C60A-47B7-8811-5442F519F47C</key>
<string>https://github.com/MosheBerman/Sufboard.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>Surfboard.xcodeproj/project.xcworkspace</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>40FF93D2-C60A-47B7-8811-5442F519F47C</key>
<string>../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/MosheBerman/Sufboard.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>110</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>40FF93D2-C60A-47B7-8811-5442F519F47C</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>40FF93D2-C60A-47B7-8811-5442F519F47C</string>
<key>IDESourceControlWCCName</key>
<string>Surfboard</string>
</dict>
</array>
</dict>
</plist>
================================================
FILE: Surfboard.xcodeproj/project.xcworkspace/xcuserdata/moshe.xcuserdatad/WorkspaceSettings.xcsettings
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
<true/>
<key>SnapshotAutomaticallyBeforeSignificantChanges</key>
<true/>
</dict>
</plist>
================================================
FILE: Surfboard.xcodeproj/xcuserdata/moshe.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
<BreakpointContent
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
scope = "0"
stopOnStyle = "0">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>
================================================
FILE: Surfboard.xcodeproj/xcuserdata/moshe.xcuserdatad/xcschemes/Surfboard.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0810"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "462C107F1993E21600A36CF8"
BuildableName = "Surfboard.app"
BlueprintName = "Surfboard"
ReferencedContainer = "container:Surfboard.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "462C10951993E21600A36CF8"
BuildableName = "SurfboardTests.xctest"
BlueprintName = "SurfboardTests"
ReferencedContainer = "container:Surfboard.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "462C107F1993E21600A36CF8"
BuildableName = "Surfboard.app"
BlueprintName = "Surfboard"
ReferencedContainer = "container:Surfboard.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "462C107F1993E21600A36CF8"
BuildableName = "Surfboard.app"
BlueprintName = "Surfboard"
ReferencedContainer = "container:Surfboard.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "462C107F1993E21600A36CF8"
BuildableName = "Surfboard.app"
BlueprintName = "Surfboard"
ReferencedContainer = "container:Surfboard.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: Surfboard.xcodeproj/xcuserdata/moshe.xcuserdatad/xcschemes/xcschememanagement.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Surfboard.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>462C107F1993E21600A36CF8</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>462C10951993E21600A36CF8</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
gitextract_a9p5efbe/
├── .gitignore
├── Icon/
│ └── Icon.psd
├── LICENSE
├── README.md
├── Surfboard/
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Base.lproj/
│ │ └── Main.storyboard
│ ├── Images.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── LaunchImage.launchimage/
│ │ │ └── Contents.json
│ │ └── iTunesArtwork.imageset/
│ │ └── Contents.json
│ ├── Info.plist
│ ├── SRFSurfboard/
│ │ ├── Panel Cells/
│ │ │ ├── Button Extension/
│ │ │ │ ├── UIButton+IndexPath.h
│ │ │ │ └── UIButton+IndexPath.m
│ │ │ └── Default Cell/
│ │ │ ├── SRFSurfboardPanelCell.h
│ │ │ ├── SRFSurfboardPanelCell.m
│ │ │ └── SRFSurfboardPanelCell.xib
│ │ ├── Panel Data Model/
│ │ │ ├── SRFSurfboardPanel.h
│ │ │ ├── SRFSurfboardPanel.m
│ │ │ └── SRFSurfboardPanelType.h
│ │ ├── SRFSurfboard.h
│ │ ├── Surfboard Delegate Protocol/
│ │ │ └── SRFSurfboardDelegate.h
│ │ └── Surfboard View Controller/
│ │ ├── SRFSurfboardViewController.h
│ │ └── SRFSurfboardViewController.m
│ ├── Sample Data/
│ │ ├── Surfboard.xcassets/
│ │ │ ├── check.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── github.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── happy.imageset/
│ │ │ │ ├── Contents.json
│ │ │ │ └── happy@2x.psd
│ │ │ ├── iPhone.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── panels.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── surfbort.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── surfer.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── swipe.imageset/
│ │ │ │ └── Contents.json
│ │ │ └── twitter.imageset/
│ │ │ └── Contents.json
│ │ └── panels.json
│ ├── ViewController.h
│ ├── ViewController.m
│ └── main.m
├── Surfboard.podspec
└── Surfboard.xcodeproj/
├── project.pbxproj
├── project.xcworkspace/
│ ├── contents.xcworkspacedata
│ ├── xcshareddata/
│ │ └── Surfboard.xccheckout
│ └── xcuserdata/
│ └── moshe.xcuserdatad/
│ ├── UserInterfaceState.xcuserstate
│ └── WorkspaceSettings.xcsettings
└── xcuserdata/
└── moshe.xcuserdatad/
├── xcdebugger/
│ └── Breakpoints_v2.xcbkptlist
└── xcschemes/
├── Surfboard.xcscheme
└── xcschememanagement.plist
Condensed preview — 47 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (97K chars).
[
{
"path": ".gitignore",
"chars": 282,
"preview": "# CocoaPods\n#\n# We recommend against adding the Pods directory to your .gitignore. However\n# you should judge for yourse"
},
{
"path": "LICENSE",
"chars": 1072,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Moshe\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "README.md",
"chars": 2569,
"preview": "Surfboard\n========\n\nSurfboard is a delightful onboarding library for iOS.\n\nScreenshots\n---\n 2014 Moshe Berman. All rights re"
},
{
"path": "Surfboard/AppDelegate.m",
"chars": 2041,
"preview": "//\n// AppDelegate.m\n// Surfboard\n//\n// Created by Moshe on 8/7/14.\n// Copyright (c) 2014 Moshe Berman. All rights re"
},
{
"path": "Surfboard/Base.lproj/Main.storyboard",
"chars": 6212,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "Surfboard/Images.xcassets/AppIcon.appiconset/Contents.json",
"chars": 1868,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"size\" : \"20x20\",\n \"scale\" : \"2x\"\n },\n {\n \"idiom\""
},
{
"path": "Surfboard/Images.xcassets/Contents.json",
"chars": 62,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n }\n}"
},
{
"path": "Surfboard/Images.xcassets/LaunchImage.launchimage/Contents.json",
"chars": 3279,
"preview": "{\n \"images\" : [\n {\n \"orientation\" : \"portrait\",\n \"idiom\" : \"iphone\",\n \"extent\" : \"full-screen\",\n "
},
{
"path": "Surfboard/Images.xcassets/iTunesArtwork.imageset/Contents.json",
"chars": 353,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"iTunesArtwork.png\"\n },\n"
},
{
"path": "Surfboard/Info.plist",
"chars": 1628,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Surfboard/SRFSurfboard/Panel Cells/Button Extension/UIButton+IndexPath.h",
"chars": 461,
"preview": "//\n// UIButton+IndexPath.h\n// Surfboard\n//\n// Created by Moshe on 8/13/14.\n// Copyright (c) 2014 Moshe Berman. All r"
},
{
"path": "Surfboard/SRFSurfboard/Panel Cells/Button Extension/UIButton+IndexPath.m",
"chars": 770,
"preview": "//\n// UIButton+IndexPath.m\n// Surfboard\n//\n// Created by Moshe on 8/13/14.\n// Copyright (c) 2014 Moshe Berman. All r"
},
{
"path": "Surfboard/SRFSurfboard/Panel Cells/Default Cell/SRFSurfboardPanelCell.h",
"chars": 679,
"preview": "//\n// SRFSurfboardPanel.h\n// Surfboard\n//\n// Created by Moshe on 8/12/14.\n// Copyright (c) 2014 Moshe Berman. All ri"
},
{
"path": "Surfboard/SRFSurfboard/Panel Cells/Default Cell/SRFSurfboardPanelCell.m",
"chars": 2049,
"preview": "//\n// SRFSurfboardPanel.m\n// Surfboard\n//\n// Created by Moshe on 8/12/14.\n// Copyright (c) 2014 Moshe Berman. All ri"
},
{
"path": "Surfboard/SRFSurfboard/Panel Cells/Default Cell/SRFSurfboardPanelCell.xib",
"chars": 7665,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
},
{
"path": "Surfboard/SRFSurfboard/Panel Data Model/SRFSurfboardPanel.h",
"chars": 1109,
"preview": "//\n// SRFSurfboardPanel.h\n// Surfboard\n//\n// Created by Moshe on 8/12/14.\n// Copyright (c) 2014 Moshe Berman. All ri"
},
{
"path": "Surfboard/SRFSurfboard/Panel Data Model/SRFSurfboardPanel.m",
"chars": 1195,
"preview": "//\n// SRFSurfboardPanel.m\n// Surfboard\n//\n// Created by Moshe on 8/12/14.\n// Copyright (c) 2014 Moshe Berman. All ri"
},
{
"path": "Surfboard/SRFSurfboard/Panel Data Model/SRFSurfboardPanelType.h",
"chars": 463,
"preview": "//\n// SRFSurfboardPanelType.h\n// Surfboard\n//\n// Created by Moshe Berman on 12/11/16.\n// Copyright © 2016 Moshe Berm"
},
{
"path": "Surfboard/SRFSurfboard/SRFSurfboard.h",
"chars": 462,
"preview": "//\n// SRFSurfboard.h\n// Surfboard\n//\n// Created by Moshe on 8/13/14.\n// Copyright (c) 2014 Moshe Berman. All rights "
},
{
"path": "Surfboard/SRFSurfboard/Surfboard Delegate Protocol/SRFSurfboardDelegate.h",
"chars": 1167,
"preview": "//\n// SRFSurfboardDelegate.h\n// Surfboard\n//\n// Created by Moshe on 8/13/14.\n// Copyright (c) 2014 Moshe Berman. All"
},
{
"path": "Surfboard/SRFSurfboard/Surfboard View Controller/SRFSurfboardViewController.h",
"chars": 1996,
"preview": "//\n// MBSurfboardViewController.h\n// Surfboard\n//\n// Created by Moshe on 8/10/14.\n// Copyright (c) 2014 Moshe Berman"
},
{
"path": "Surfboard/SRFSurfboard/Surfboard View Controller/SRFSurfboardViewController.m",
"chars": 16586,
"preview": "//\n// MBSurfboardViewController.m\n// Surfboard\n//\n// Created by Moshe on 8/10/14.\n// Copyright (c) 2014 Moshe Berman"
},
{
"path": "Surfboard/Sample Data/Surfboard.xcassets/check.imageset/Contents.json",
"chars": 337,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"arrow.png\"\n },\n {\n "
},
{
"path": "Surfboard/Sample Data/Surfboard.xcassets/github.imageset/Contents.json",
"chars": 339,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"github.png\"\n },\n {\n "
},
{
"path": "Surfboard/Sample Data/Surfboard.xcassets/happy.imageset/Contents.json",
"chars": 337,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"happy.png\"\n },\n {\n "
},
{
"path": "Surfboard/Sample Data/Surfboard.xcassets/iPhone.imageset/Contents.json",
"chars": 385,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"iPhone.png\"\n },\n {\n "
},
{
"path": "Surfboard/Sample Data/Surfboard.xcassets/panels.imageset/Contents.json",
"chars": 339,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"panels.png\"\n },\n {\n "
},
{
"path": "Surfboard/Sample Data/Surfboard.xcassets/surfbort.imageset/Contents.json",
"chars": 381,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"surfbort.png\"\n },\n {"
},
{
"path": "Surfboard/Sample Data/Surfboard.xcassets/surfer.imageset/Contents.json",
"chars": 339,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"surfer.png\"\n },\n {\n "
},
{
"path": "Surfboard/Sample Data/Surfboard.xcassets/swipe.imageset/Contents.json",
"chars": 337,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"swipe.png\"\n },\n {\n "
},
{
"path": "Surfboard/Sample Data/Surfboard.xcassets/twitter.imageset/Contents.json",
"chars": 341,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"twitter.png\"\n },\n {\n"
},
{
"path": "Surfboard/Sample Data/panels.json",
"chars": 575,
"preview": "[\n {\n \"text\" : \"Welcome to Surfboard.\",\n \"image\" : \"swipe\",\n \"show-device\" : true\n },\n {\n \"text\" : \"Surfboard makes it d"
},
{
"path": "Surfboard/ViewController.h",
"chars": 213,
"preview": "//\n// ViewController.h\n// Surfboard\n//\n// Created by Moshe on 8/7/14.\n// Copyright (c) 2014 Moshe Berman. All rights"
},
{
"path": "Surfboard/ViewController.m",
"chars": 2148,
"preview": "//\n// ViewController.m\n// Surfboard\n//\n// Created by Moshe on 8/7/14.\n// Copyright (c) 2014 Moshe Berman. All rights"
},
{
"path": "Surfboard/main.m",
"chars": 332,
"preview": "//\n// main.m\n// Surfboard\n//\n// Created by Moshe on 8/7/14.\n// Copyright (c) 2014 Moshe Berman. All rights reserved."
},
{
"path": "Surfboard.podspec",
"chars": 849,
"preview": "Pod::Spec.new do |s|\n\n s.name = \"Surfboard\"\n s.version = \"1.0.0\"\n s.summary = \"Surfboard is a delig"
},
{
"path": "Surfboard.xcodeproj/project.pbxproj",
"chars": 18389,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "Surfboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 154,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:Surfboard.xcode"
},
{
"path": "Surfboard.xcodeproj/project.xcworkspace/xcshareddata/Surfboard.xccheckout",
"chars": 1494,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Surfboard.xcodeproj/project.xcworkspace/xcuserdata/moshe.xcuserdatad/WorkspaceSettings.xcsettings",
"chars": 332,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Surfboard.xcodeproj/xcuserdata/moshe.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist",
"chars": 473,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Bucket\n type = \"1\"\n version = \"2.0\">\n <Breakpoints>\n <BreakpointProxy"
},
{
"path": "Surfboard.xcodeproj/xcuserdata/moshe.xcuserdatad/xcschemes/Surfboard.xcscheme",
"chars": 3763,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0810\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "Surfboard.xcodeproj/xcuserdata/moshe.xcuserdatad/xcschemes/xcschememanagement.plist",
"chars": 571,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
}
]
// ... and 3 more files (download for full content)
About this extraction
This page contains the full source code of the MosheBerman/Surfboard GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 47 files (29.9 MB), approximately 25.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.