Ind.ie is now Small Technology Foundation.
Commit d1830e97 authored by Aral Balkan's avatar Aral Balkan

Initial add.

parents
#########################
# .gitignore file for Xcode4 and Xcode5 Source projects
#
# Apple bugs, waiting for Apple to fix/respond:
#
# 15564624 - what does the xccheckout file in Xcode5 do? Where's the documentation?
#
# Version 2.6
# For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects
#
# 2015 updates:
# - Fixed typo in "xccheckout" line - thanks to @lyck for pointing it out!
# - Fixed the .idea optional ignore. Thanks to @hashier for pointing this out
# - Finally added "xccheckout" to the ignore. Apple still refuses to answer support requests about this, but in practice it seems you should ignore it.
# - minor tweaks from Jona and Coeur (slightly more precise xc* filtering/names)
# 2014 updates:
# - appended non-standard items DISABLED by default (uncomment if you use those tools)
# - removed the edit that an SO.com moderator made without bothering to ask me
# - researched CocoaPods .lock more carefully, thanks to Gokhan Celiker
# 2013 updates:
# - fixed the broken "save personal Schemes"
# - added line-by-line explanations for EVERYTHING (some were missing)
#
# NB: if you are storing "built" products, this WILL NOT WORK,
# and you should use a different .gitignore (or none at all)
# This file is for SOURCE projects, where there are many extra
# files that we want to exclude
#
#########################
#####
# OS X temporary files that should never be committed
#
# c.f. http://www.westwind.com/reference/os-x/invisibles.html
.DS_Store
# c.f. http://www.westwind.com/reference/os-x/invisibles.html
.Trashes
# c.f. http://www.westwind.com/reference/os-x/invisibles.html
*.swp
#
# *.lock - this is used and abused by many editors for many different things.
# For the main ones I use (e.g. Eclipse), it should be excluded
# from source-control, but YMMV.
# (lock files are usually local-only file-synchronization on the local FS that should NOT go in git)
# c.f. the "OPTIONAL" section at bottom though, for tool-specific variations!
#
# In particular, if you're using CocoaPods, you'll want to comment-out this line:
*.lock
#
# profile - REMOVED temporarily (on double-checking, I can't find it in OS X docs?)
#profile
####
# Xcode temporary files that should never be committed
#
# NB: NIB/XIB files still exist even on Storyboard projects, so we want this...
*~.nib
####
# Xcode build files -
#
# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData"
DerivedData/
# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build"
build/
#####
# Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups)
#
# This is complicated:
#
# SOMETIMES you need to put this file in version control.
# Apple designed it poorly - if you use "custom executables", they are
# saved in this file.
# 99% of projects do NOT use those, so they do NOT want to version control this file.
# ..but if you're in the 1%, comment out the line "*.pbxuser"
# .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html
*.pbxuser
# .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html
*.mode1v3
# .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html
*.mode2v3
# .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file
*.perspectivev3
# NB: also, whitelist the default ones, some projects need to use these
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
####
# Xcode 4 - semi-personal settings
#
# Apple Shared data that Apple put in the wrong folder
# c.f. http://stackoverflow.com/a/19260712/153422
# FROM ANSWER: Apple says "don't ignore it"
# FROM COMMENTS: Apple is wrong; Apple code is too buggy to trust; there are no known negative side-effects to ignoring Apple's unofficial advice and instead doing the thing that actively fixes bugs in Xcode
# Up to you, but ... current advice: ignore it.
*.xccheckout
#
#
# OPTION 1: ---------------------------------
# throw away ALL personal settings (including custom schemes!
# - unless they are "shared")
# As per build/ and DerivedData/, this ought to have a trailing slash
#
# NB: this is exclusive with OPTION 2 below
xcuserdata/
# OPTION 2: ---------------------------------
# get rid of ALL personal settings, but KEEP SOME OF THEM
# - NB: you must manually uncomment the bits you want to keep
#
# NB: this *requires* git v1.8.2 or above; you may need to upgrade to latest OS X,
# or manually install git over the top of the OS X version
# NB: this is exclusive with OPTION 1 above
#
#xcuserdata/**/*
# (requires option 2 above): Personal Schemes
#
#!xcuserdata/**/xcschemes/*
####
# XCode 4 workspaces - more detailed
#
# Workspaces are important! They are a core feature of Xcode - don't exclude them :)
#
# Workspace layout is quite spammy. For reference:
#
# /(root)/
# /(project-name).xcodeproj/
# project.pbxproj
# /project.xcworkspace/
# contents.xcworkspacedata
# /xcuserdata/
# /(your name)/xcuserdatad/
# UserInterfaceState.xcuserstate
# /xcshareddata/
# /xcschemes/
# (shared scheme name).xcscheme
# /xcuserdata/
# /(your name)/xcuserdatad/
# (private scheme).xcscheme
# xcschememanagement.plist
#
#
####
# Xcode 4 - Deprecated classes
#
# Allegedly, if you manually "deprecate" your classes, they get moved here.
#
# We're using source-control, so this is a "feature" that we do not want!
*.moved-aside
####
# OPTIONAL: Some well-known tools that people use side-by-side with Xcode / iOS development
#
# NB: I'd rather not include these here, but gitignore's design is weak and doesn't allow
# modular gitignore: you have to put EVERYTHING in one file.
#
# COCOAPODS:
#
# c.f. http://guides.cocoapods.org/using/using-cocoapods.html#what-is-a-podfilelock
# c.f. http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
#!Podfile.lock
#
# RUBY:
#
# c.f. http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
#
#!Gemfile.lock
#
# IDEA:
#
# c.f. https://www.jetbrains.com/objc/help/managing-projects-under-version-control.html?search=workspace.xml
#
#.idea/workspace.xml
#
# TEXTMATE:
#
# -- UNVERIFIED: c.f. http://stackoverflow.com/a/50283/153422
#
#tm_build_errors
####
# UNKNOWN: recommended by others, but I can't discover what these files are
#
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:StatusMessageWindowDemo/StatusMessageWindowDemo.xcodeproj">
</FileRef>
<FileRef
location = "group:StatusMessageWindow/StatusMessageWindow.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A7C0E9CC1C221B9800110179"
BuildableName = "StatusMessageWindow.framework"
BlueprintName = "StatusMessageWindow"
ReferencedContainer = "container:StatusMessageWindow.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<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">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A7C0E9CC1C221B9800110179"
BuildableName = "StatusMessageWindow.framework"
BlueprintName = "StatusMessageWindow"
ReferencedContainer = "container:StatusMessageWindow.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A7C0E9CC1C221B9800110179"
BuildableName = "StatusMessageWindow.framework"
BlueprintName = "StatusMessageWindow"
ReferencedContainer = "container:StatusMessageWindow.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
//
// CCHLinkGestureRecognizer.h
// CCHLinkTextView
//
// Copyright (C) 2014 Claus Höfele
//
// 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.
//
#import <UIKit/UIKit.h>
/** Type of result of the gesture in state UIGestureRecognizerStateRecognized. */
typedef enum {
CCHLinkGestureRecognizerResultUnknown,
CCHLinkGestureRecognizerResultTap,
CCHLinkGestureRecognizerResultLongPress,
CCHLinkGestureRecognizerResultFailed
} CCHLinkGestureRecognizerResult;
/**
A discreet gesture recognizer that sends action messages for touch down
(UIGestureRecognizerStateBegan), touch up for a tap (UIGestureRecognizerStateRecognized,
CCHLinkGestureRecognizerResultTap), touch up for a long press (UIGestureRecognizerStateRecognized,
CCHLinkGestureRecognizerResultLongPress), and touch up when the gesture has failed
(UIGestureRecognizerStateRecognized, CCHLinkGestureRecognizerResultFailed).
*/
@interface CCHLinkGestureRecognizer : UIGestureRecognizer
/** The minimum period fingers must press on the view for the gesture to be recognized as a long press (default = 0.5s). */
@property (nonatomic) CFTimeInterval minimumPressDuration;
/** The maximum movement of the fingers on the view before the gesture gets recognized as failed (default = 10 points). */
@property (nonatomic) CGFloat allowableMovement;
/** Result code of the gesture when the gesture has been recognized (state is UIGestureRecognizerStateRecognized). */
@property (nonatomic, readonly) CCHLinkGestureRecognizerResult result;
@end
//
// CCHLinkGestureRecognizer.m
// CCHLinkTextView
//
// Copyright (C) 2014 Claus Höfele
//
// 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.
//
#import "CCHLinkGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
@interface CCHLinkGestureRecognizer ()
@property (nonatomic) CCHLinkGestureRecognizerResult result;
@property (nonatomic) CGPoint initialPoint;
@property (nonatomic) NSTimer *timer;
@end
// disable long tap
@implementation CCHLinkGestureRecognizer
- (instancetype)init
{
self = [super init];
if (self) {
[self setUp];
}
return self;
}
- (instancetype)initWithTarget:(id)target action:(SEL)action
{
self = [super initWithTarget:target action:action];
if (self) {
[self setUp];
}
return self;
}
- (void)setUp
{
// Same defaults as UILongPressGestureRecognizer
self.minimumPressDuration = 0.5;
self.allowableMovement = 10;
self.result = CCHLinkGestureRecognizerResultUnknown;
self.initialPoint = CGPointZero;
}
- (void)reset
{
[super reset];
self.result = CCHLinkGestureRecognizerResultUnknown;
self.initialPoint = CGPointZero;
[self.timer invalidate];
self.timer = nil;
}
- (void)longPressed:(NSTimer *)timer
{
[timer invalidate];
self.result = CCHLinkGestureRecognizerResultLongPress;
self.state = UIGestureRecognizerStateRecognized;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
NSAssert(self.result == CCHLinkGestureRecognizerResultUnknown, @"Invalid result state");
UITouch *touch = touches.anyObject;
self.initialPoint = [touch locationInView:self.view];
self.state = UIGestureRecognizerStateBegan;
self.timer = [NSTimer scheduledTimerWithTimeInterval:self.minimumPressDuration target:self selector:@selector(longPressed:) userInfo:nil repeats:NO];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
if (![self touchIsCloseToInitialPoint:touches.anyObject]) {
self.result = CCHLinkGestureRecognizerResultFailed;
self.state = UIGestureRecognizerStateRecognized;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
if ([self touchIsCloseToInitialPoint:touches.anyObject]) {
self.result = CCHLinkGestureRecognizerResultTap;
self.state = UIGestureRecognizerStateRecognized;
} else {
self.result = CCHLinkGestureRecognizerResultFailed;
self.state = UIGestureRecognizerStateRecognized;
}
}
- (BOOL)touchIsCloseToInitialPoint:(UITouch *)touch
{
CGPoint point = [touch locationInView:self.view];
CGFloat xDistance = (self.initialPoint.x - point.x);
CGFloat yDistance = (self.initialPoint.y - point.y);
CGFloat squaredDistance = (xDistance * xDistance) + (yDistance * yDistance);
BOOL isClose = (squaredDistance <= (self.allowableMovement * self.allowableMovement));
return isClose;
}
@end
//
// CCHLinkTextView.h
// CCHLinkTextView
//
// Copyright (C) 2014 Claus Höfele
//
// 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.
//
#import <UIKit/UIKit.h>
@class CCHLinkGestureRecognizer;
@protocol CCHLinkTextViewDelegate;
/** Attribute name for links. The value can by any object.*/
extern NSString *const CCHLinkAttributeName;
/** `UITextView` subclass with tappable links. */
@interface CCHLinkTextView : UITextView
/** Delegate to receive tap and long press events. */
@property (nonatomic, weak) id<CCHLinkTextViewDelegate> linkDelegate;
/** `NSAttributedString` attributes applied to links when touched. Use `linkTextAttributes` for attributes applied to links before being touched.*/
@property (nonatomic, copy) NSDictionary *linkTextTouchAttributes;
/** The minimum period fingers must press on the link for the gesture to be recognized as a long press (default = 0.5s). */
@property (nonatomic) CFTimeInterval minimumPressDuration;
/** The maximum movement of the fingers on the link before the gesture is ignored (default = 10 points). */
@property (nonatomic) CGFloat allowableMovement;
/** Expands or shrinks the tap area of the link text (default: {-5, -5, -5, -5}). */
@property (nonatomic) UIEdgeInsets tapAreaInsets;
/** The gesture recognizer used to detect links in this text view. */
@property (nonatomic, readonly) CCHLinkGestureRecognizer *linkGestureRecognizer;
/** The corner radius of the rounded rectangle that is shown when the link is touched. Set to 0
to disable rounder corners (default = 0 points). */
@property (nonatomic) CGFloat linkCornerRadius;
/**
For the given ranges, enumerates all view rectangles that cover each range.
@param ranges array of ranges.
@param block block that's called for each view rect.
*/
- (void)enumerateViewRectsForRanges:(NSArray *)ranges usingBlock:(void (^)(CGRect rect, NSRange range, BOOL *stop))block;
/**
Enumerates all ranges with a link for the given point.
@param location point in this view's coordinates.
@param block block that's called for every link range that was found.
*/
- (BOOL)enumerateLinkRangesContainingLocation:(CGPoint)location usingBlock:(void (^)(NSRange range))block;
@end
This diff is collapsed.
//
// CCHLinkTextViewDelegate.h
// CCHLinkTextView
//
// Copyright (C) 2014 Claus Höfele
//
// 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.
//
#import <Foundation/Foundation.h>
@class CCHLinkTextView;
/** Methods called by `CCHLinkTextView` when links are tapped or long pressed. */
@protocol CCHLinkTextViewDelegate <NSObject>
@optional
/** The user has tapped on a link. */
- (void)linkTextView:(CCHLinkTextView *)linkTextView didTapLinkWithValue:(id)value;
/** The user has long pressed a link. */
- (void)linkTextView:(CCHLinkTextView *)linkTextView didLongPressLinkWithValue:(id)value;
@end
//////////////////////////////////////////////////////////////////////////////////////////
//
// Handle
//
// Provide a simple, human interface for handling notifications.
//
// Copyright © 2015 Aral Balkan.
// Released with ♥ by Ind.ie under the MIT License.
//
//////////////////////////////////////////////////////////////////////////////////////////
import Foundation
let 📡:NSNotificationCenter = NSNotificationCenter.defaultCenter()
public typealias NotificationBlock = (NSNotification!) -> Void
// Utility method: destroy a notification handler
func destroyNotificationHandler(inout handler:NotificationHandler?)
{
handler!.remove()
handler = nil
}
//
// MARK: - Class
//
public class NotificationHandler:NSObject
{
var handler:NotificationBlock
init(handler:NotificationBlock)
{
self.handler = handler
}
public func handlerProxy(notification:NSNotification!)
{
handler(notification)
}
public func remove()
{
📡.removeObserver(self)
}
}
////////////////////////////////////////////////////////////////////////////////
//
// MARK: - Closure-based API (preferred)
//
////////////////////////////////////////////////////////////////////////////////
//
// MARK: - Handling notifications
//
// (When you no longer need the returned notification handler, call its
// remove method. For example, you can define the notification handler
// in a view controller’s viewWillAppear() method and remove it in its
// viewWillDisappear() method.
//
public func handle(notificationName:String, with block:NotificationBlock) -> NotificationHandler
{
//
// Instead of using the block-based methods of NSNotificationCenter, create an observer
// object and return that for easy removing of the observer, etc.
//
let observer = NotificationHandler(handler: block)
📡.addObserver(observer, selector: "handlerProxy:", name: notificationName, object: nil)
return observer
}
public func handle(notificationName:String, from object:AnyObject?, with block:NotificationBlock) -> NotificationHandler
{
let observer = NotificationHandler(handler: block)
📡.addObserver(observer, selector: "handlerProxy:", name: notificationName, object: object)
return observer
}
//
// MARK: - Posting notifications
//
public func post(notificationName:String, from object:AnyObject?, with userInfo:[NSObject : AnyObject]?)
{
// println("About to post: \(notificationName) notification from \(object) with userInfo: \(userInfo)")
📡.post(notificationName, from: object, with: userInfo)
}
public func post(notificationName:String, from object:AnyObject?)