Skip to content

Commit

Permalink
1. Taptic snippet working to enable haptic feedback.
Browse files Browse the repository at this point in the history
2. Added Taptic functionality to Snippet scroll.

3. TapticGenerator has options for all types of haptic feedback.
  • Loading branch information
elliott-io committed Apr 29, 2020
1 parent 93a7d61 commit ca12bd1
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 10 deletions.
File renamed without changes.
Binary file added README/Swift-Snippets-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README/Swift-Snippets-logo.xcf
Binary file not shown.
20 changes: 18 additions & 2 deletions StoryboardSnippets/StoryboardSnippets.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
0105EC0024563E55002B38A4 /* AlwaysPresentAsPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105EBFF24563E55002B38A4 /* AlwaysPresentAsPopover.swift */; };
0105EC0224563ECE002B38A4 /* PopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105EC0124563ECE002B38A4 /* PopoverViewController.swift */; };
0105EC0424568719002B38A4 /* PopoverPlacementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105EC0324568719002B38A4 /* PopoverPlacementViewController.swift */; };
0105EC0724591EA9002B38A4 /* TapticViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105EC0624591EA9002B38A4 /* TapticViewController.swift */; };
0105EC0B245947F0002B38A4 /* TapticGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105EC0A245947F0002B38A4 /* TapticGenerator.swift */; };
017E1F062454E01000D5362E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017E1F052454E01000D5362E /* AppDelegate.swift */; };
017E1F082454E01000D5362E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017E1F072454E01000D5362E /* SceneDelegate.swift */; };
017E1F0D2454E01000D5362E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 017E1F0B2454E01000D5362E /* Main.storyboard */; };
Expand Down Expand Up @@ -51,6 +53,8 @@
0105EBFF24563E55002B38A4 /* AlwaysPresentAsPopover.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlwaysPresentAsPopover.swift; sourceTree = "<group>"; };
0105EC0124563ECE002B38A4 /* PopoverViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopoverViewController.swift; sourceTree = "<group>"; };
0105EC0324568719002B38A4 /* PopoverPlacementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverPlacementViewController.swift; sourceTree = "<group>"; };
0105EC0624591EA9002B38A4 /* TapticViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapticViewController.swift; sourceTree = "<group>"; };
0105EC0A245947F0002B38A4 /* TapticGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapticGenerator.swift; sourceTree = "<group>"; };
017E1F022454E01000D5362E /* Swift Snippets.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Swift Snippets.app"; sourceTree = BUILT_PRODUCTS_DIR; };
017E1F052454E01000D5362E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
017E1F072454E01000D5362E /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -121,6 +125,15 @@
path = PopoverMessage;
sourceTree = "<group>";
};
0105EC0524591E7E002B38A4 /* Taptic */ = {
isa = PBXGroup;
children = (
0105EC0624591EA9002B38A4 /* TapticViewController.swift */,
0105EC0A245947F0002B38A4 /* TapticGenerator.swift */,
);
path = Taptic;
sourceTree = "<group>";
};
017E1EF92454E01000D5362E = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -178,6 +191,7 @@
017E1F50245530A200D5362E /* Snippets */ = {
isa = PBXGroup;
children = (
0105EC0524591E7E002B38A4 /* Taptic */,
0105EBFE24563E15002B38A4 /* PopoverMessage */,
017E1F512455316600D5362E /* SavePhotoCustomAlbum */,
017E1F4624552B2900D5362E /* snippets.json */,
Expand Down Expand Up @@ -340,9 +354,11 @@
017E1F3B2454FBE800D5362E /* SnippetCell.swift in Sources */,
017E1F082454E01000D5362E /* SceneDelegate.swift in Sources */,
017E1F4D24552E4800D5362E /* Snippet.swift in Sources */,
0105EC0B245947F0002B38A4 /* TapticGenerator.swift in Sources */,
017E1F432455162500D5362E /* PhotoCell.swift in Sources */,
017E1F412455157700D5362E /* GalleryViewController.swift in Sources */,
017E1F392454F80B00D5362E /* SnippetsViewController.swift in Sources */,
0105EC0724591EA9002B38A4 /* TapticViewController.swift in Sources */,
017E1F3D2454FDDD00D5362E /* Extensions.swift in Sources */,
0105EC0424568719002B38A4 /* PopoverPlacementViewController.swift in Sources */,
0105EC0024563E55002B38A4 /* AlwaysPresentAsPopover.swift in Sources */,
Expand Down Expand Up @@ -525,7 +541,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.elliottio.StoryboardSnippets;
PRODUCT_NAME = "Swift Snippets";
SWIFT_VERSION = 5.0;
Expand All @@ -544,7 +560,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.elliottio.StoryboardSnippets;
PRODUCT_NAME = "Swift Snippets";
SWIFT_VERSION = 5.0;
Expand Down
Binary file not shown.
39 changes: 34 additions & 5 deletions StoryboardSnippets/StoryboardSnippets/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<!--Snippets-->
<scene sceneID="8ML-Ek-ir2">
<objects>
<collectionViewController storyboardIdentifier="snippetsCollection" id="2gY-90-BOk" customClass="SnippetsViewController" customModule="StoryboardSnippets" customModuleProvider="target" sceneMemberID="viewController">
<collectionViewController storyboardIdentifier="snippetsCollection" id="2gY-90-BOk" customClass="SnippetsViewController" customModule="Swift_Snippets" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" keyboardDismissMode="onDrag" dataMode="prototypes" springLoaded="YES" id="yGg-lx-IrL">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
Expand All @@ -23,7 +23,7 @@
<inset key="sectionInset" minX="0.0" minY="20" maxX="0.0" maxY="20"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="snippetCell" id="Cj9-Al-mu2" customClass="SnippetCell" customModule="StoryboardSnippets" customModuleProvider="target">
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="snippetCell" id="Cj9-Al-mu2" customClass="SnippetCell" customModule="Swift_Snippets" customModuleProvider="target">
<rect key="frame" x="0.0" y="20" width="414" height="685"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<collectionViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="XIQ-kM-wdn">
Expand Down Expand Up @@ -143,7 +143,7 @@
<!--Asset Gallery-->
<scene sceneID="rbs-vY-lQK">
<objects>
<collectionViewController storyboardIdentifier="galleryCollection" id="lZK-2E-92y" customClass="GalleryViewController" customModule="StoryboardSnippets" customModuleProvider="target" sceneMemberID="viewController">
<collectionViewController storyboardIdentifier="galleryCollection" id="lZK-2E-92y" customClass="GalleryViewController" customModule="Swift_Snippets" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" keyboardDismissMode="onDrag" dataMode="prototypes" springLoaded="YES" id="XOt-2Q-OVz">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
Expand All @@ -155,7 +155,7 @@
<inset key="sectionInset" minX="0.0" minY="20" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="photoCell" id="LBW-Oh-NGg" customClass="PhotoCell" customModule="StoryboardSnippets" customModuleProvider="target">
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="photoCell" id="LBW-Oh-NGg" customClass="PhotoCell" customModule="Swift_Snippets" customModuleProvider="target">
<rect key="frame" x="0.0" y="20" width="414" height="354"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<collectionViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="mzb-h4-ADm">
Expand Down Expand Up @@ -322,10 +322,38 @@
</objects>
<point key="canvasLocation" x="1841" y="870"/>
</scene>
<!--Taptic Feedback-->
<scene sceneID="d3R-1f-LLZ">
<objects>
<viewController storyboardIdentifier="taptic" title="Taptic Feedback" id="G3V-Be-TxT" customClass="TapticViewController" customModule="Swift_Snippets" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="SPI-cA-D2C">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GgY-Ei-rFy">
<rect key="frame" x="0.0" y="44" width="414" height="216"/>
</pickerView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="GgY-Ei-rFy" firstAttribute="top" secondItem="kL9-d6-n7m" secondAttribute="top" id="8fP-JT-mXb"/>
<constraint firstItem="GgY-Ei-rFy" firstAttribute="centerX" secondItem="SPI-cA-D2C" secondAttribute="centerX" id="LnX-Ag-i6w"/>
<constraint firstItem="GgY-Ei-rFy" firstAttribute="leading" secondItem="kL9-d6-n7m" secondAttribute="leading" id="swH-Ws-cbu"/>
</constraints>
<viewLayoutGuide key="safeArea" id="kL9-d6-n7m"/>
</view>
<connections>
<outlet property="picker" destination="GgY-Ei-rFy" id="lvq-Su-lpX"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="GIp-Jc-PcC" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1841" y="1610"/>
</scene>
<!--Popover View Controller-->
<scene sceneID="6We-We-0XM">
<objects>
<viewController storyboardIdentifier="popover" id="3zl-Xh-u1j" customClass="PopoverViewController" customModule="GroupComposition" customModuleProvider="target" sceneMemberID="viewController">
<viewController storyboardIdentifier="popover" id="3zl-Xh-u1j" customClass="PopoverViewController" customModule="Swift_Snippets" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="G1E-j0-F6L">
<rect key="frame" x="0.0" y="0.0" width="260" height="120"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
Expand All @@ -342,6 +370,7 @@
<constraint firstItem="Ugr-ML-C57" firstAttribute="centerY" secondItem="G1E-j0-F6L" secondAttribute="centerY" placeholder="YES" id="95H-p2-avg"/>
<constraint firstAttribute="trailing" secondItem="Ugr-ML-C57" secondAttribute="trailing" constant="16" placeholder="YES" id="KXC-Rt-sGj"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Cgr-gi-NJY"/>
</view>
<size key="freeformSize" width="260" height="120"/>
<connections>
Expand Down
11 changes: 9 additions & 2 deletions StoryboardSnippets/StoryboardSnippets/Helpers/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ public extension UICollectionViewFlowLayout {

let newVerticalOffset = ((currentPage + flickedPages) * pageHeight) - collectionView.contentInset.top

// give haptic feedback based on how many cells are scrolls
if abs(flickedPages) > 1 {
TapticGenerator.notification(.success)
} else {
TapticGenerator.impact(.medium)
}

return CGPoint(x: proposedContentOffset.x, y: newVerticalOffset - collectionView.safeAreaInsets.top)
}
}
Expand All @@ -58,7 +65,7 @@ extension UIApplication {
extension UIViewController {
static var existingPopover: PopoverViewController?

func showPopover(message: String, sourceView: UIView, sourceRect: CGRect, arrowDirection: UIPopoverArrowDirection = [], passthroughViews: [UIView]? = nil) {
func showPopover(message: String, sourceView: UIView, sourceRect: CGRect, arrowDirection: UIPopoverArrowDirection = [], passthroughViews: [UIView]? = []) {
DispatchQueue.main.async {

// init view controller
Expand All @@ -84,7 +91,7 @@ extension UIViewController {
if let views = passthroughViews {
presentationController.passthroughViews = views
}

// if there is an existing presenting view controller, dismiss it before presenting this popover
if let existing = UIViewController.existingPopover {
existing.dismiss(animated: true, completion: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class PopoverViewController: UIViewController {

@IBOutlet weak var labelMessage: UILabel!
var message:String?
var sendingViewController: UIViewController?
weak var sendingViewController: UIViewController?
var arrowDirection: UIPopoverArrowDirection = .any

let ltPadding: CGFloat = 20 // leading/trailing padding
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// TapticGenerator.swift
// StoryboardSnippets
//
// Created by Elliott Io on 4/28/20.
// Copyright © 2020 elliott io. All rights reserved.
//

import Foundation
import UIKit

class TapticGenerator {
static func impact(_ style: UIImpactFeedbackGenerator.FeedbackStyle) {
let generator = UIImpactFeedbackGenerator(style: style)
generator.impactOccurred()
}

static func notification(_ type: UINotificationFeedbackGenerator.FeedbackType) {
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(type)
}

static func selectionChanged() {
let generator = UISelectionFeedbackGenerator()
generator.selectionChanged()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// TapticGenerator.swift
// StoryboardSnippets
//
// Created by Elliott Io on 4/28/20.
// Copyright © 2020 elliott io. All rights reserved.
//

import Foundation
import UIKit

//class TapticGenerator {
//
//}
import UIKit

class TapticViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
var i = 0
@IBOutlet weak var picker: UIPickerView!
var pickerData: [String] = [String]()

override func viewDidLoad() {
super.viewDidLoad()

let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)

btn.widthAnchor.constraint(equalToConstant: 128).isActive = true
btn.heightAnchor.constraint(equalToConstant: 50).isActive = true
btn.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
btn.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

btn.setTitle("Feel Taptic", for: .normal)
btn.setTitleColor(.white, for: .normal)
btn.backgroundColor = .secondarySystemBackground
btn.layer.cornerRadius = Styles.buttonRadius
btn.addTarget(self, action: #selector(tapped), for: .touchUpInside)

// setup picker
self.picker.delegate = self
self.picker.dataSource = self
pickerData = ["Heavy", "Medium", "Light", "Selection Changed", "Success", "Warning", "Error"]
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

// columns of picker
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}

// rows of picker
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.count
}

// component is the column
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickerData[row]
}

@objc func tapped() {
switch (pickerData[picker.selectedRow(inComponent: 0)]) {
case "Heavy":
TapticGenerator.impact(.heavy)
case "Medium":
TapticGenerator.impact(.medium)
case "Light":
TapticGenerator.impact(.light)
case "Success":
TapticGenerator.notification(.success)
case "Warning":
TapticGenerator.notification(.warning)
case "Error":
TapticGenerator.notification(.error)
case "Selection Changed":
TapticGenerator.selectionChanged()
default:
TapticGenerator.selectionChanged()
}
}
}
8 changes: 8 additions & 0 deletions StoryboardSnippets/StoryboardSnippets/Snippets/snippets.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
"sourceFiles": ["PopoverViewController.swift", "AlwaysPresentAsPopover.swift"],
"vcIdentifier": "popoverPlacement"
},
{
"id": "3",
"title": "Taptic",
"subtitle": "Generates haptic feedback using Apple's Taptic Engine.",
"sourceFiles": ["TapticGenerator.swift"],
"assetImageName": "encryption",
"vcIdentifier": "taptic"
},
{
"id": "3",
"title": "Text Encryption",
Expand Down

0 comments on commit ca12bd1

Please sign in to comment.