Skip to content

Commit

Permalink
Update helper methods
Browse files Browse the repository at this point in the history
  • Loading branch information
ephread committed Jun 6, 2021
1 parent 18f4398 commit e542487
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 58 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ Instructions follows [Semantic Versioning](http://semver.org/).
Released on 2021-06-XX.

### Added
- New helper methods to deal with cutout paths that aren't anchored to a specific view.
- New helper methods to:
- deal with cutout paths that aren't anchored to a specific view;
- update all coach mark properties at once.

## [2.0.1](https://github.com/ephread/Instructions/releases/tag/2.0.1)
Released on 2021-02-07.
Expand Down
12 changes: 6 additions & 6 deletions Examples/Instructions Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@
CODE_SIGN_STYLE = Automatic;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Example/Snapshot Tests/Supporting Files/Info.plist";
Expand Down Expand Up @@ -981,7 +981,7 @@
CODE_SIGN_STYLE = Automatic;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Example/Snapshot Tests/Supporting Files/Info.plist";
Expand Down Expand Up @@ -1014,7 +1014,7 @@
CODE_SIGN_STYLE = Automatic;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build/",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Example/Unit Tests/Supporting Files/Info.plist";
Expand Down Expand Up @@ -1047,7 +1047,7 @@
CODE_SIGN_STYLE = Automatic;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build/",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Example/Unit Tests/Supporting Files/Info.plist";
Expand Down Expand Up @@ -1078,7 +1078,7 @@
CODE_SIGN_STYLE = Automatic;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Example/UI Tests/Supporting Files/Info.plist";
Expand Down Expand Up @@ -1111,7 +1111,7 @@
CODE_SIGN_STYLE = Automatic;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
"$(PROJECT_DIR)/Carthage/Build",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Example/UI Tests/Supporting Files/Info.plist";
Expand Down
9 changes: 9 additions & 0 deletions Instructions.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,11 @@ var coachMark = coachMarksController.helper.makeCoachMark(
)
```

`frame` will be the frame of `customView` converted in the `coachMarksController.view` referential, so don't have to worry about making sure the coordinates are in the appropriate referential. You can provide any kind of shape, from a simple rectangle to a complex star.
`frame` is the frame of `customView` expressed in the coordinate space of `coachMarksController.view`.
The conversion between this coordinate space and Instructions' coordinate space is handled automatically.
Any kind of shape can be provided, from a simple rectangle to a complex star.

You can also pass a frame rectangle directly if you supply the _superview_ to which it relates.
You can also pass a frame rectangle directly if you supply its coordinate space.

```swift
var coachMark = coachMarksController.helper.makeCoachMark(
Expand Down Expand Up @@ -508,15 +510,22 @@ func coachMarksController(
// Once the animation is completed, we update the coach mark,
// and start the display again. Since inout parameters cannot be
// captured by the closure, you can use the following method to update
// the coachmark. It will only work if you paused the flow.
//
// Note: it's also possible to update the coach mark by providing a
// a frame rectangle.
// the coach mark. It will only work if you paused the flow.
coachMarksController.helper.updateCurrentCoachMark(using: myView)
coachMarksController.flow.resume()
})
}
```
If you need to update multiple properties on the coach mark, you may prefer using the block-based method.
When updating points of interest and cutout paths, make sure to express them in Instructions' coordinate
space, by using the provided converter.

```
coachMarksController.helper.updateCurrentCoachMark { coachMark, converter in
coachMark.pointOfInterest = converter.convert(point: myPoint, from: myPointSuperview)
coachMark.gapBetweenCoachMarkAndCutoutPath = 6
}
```

⚠️ Since the blurring overlay snapshots the view during coach mark appearance/disappearance,
you should make sure that animations targeting your own view don't occur while a coach mark
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,26 @@ public class CoachMarkCoordinateConverter {
self.rootView = rootView
}

public func convert(frame: CGRect, from superview: UIView?) -> CGRect {
/// Converts a rectangle from the specified coordinate space
/// to the coordinate space of Instructions.
///
/// - Parameters:
/// - frame: A rectangle in the specified coordinate space.
/// - superview: The coordinate space in which `rect` is specified.
/// - Returns: A rectangle specified in the coordinate space of Instructions.
public func convert(rect: CGRect, from superview: UIView?) -> CGRect {
// No superview, assuming frame in `instructionsRootView`'s coordinate system.
guard let superview = superview else {
print(ErrorMessage.Warning.anchorViewIsNotInTheViewHierarchy)
return frame
return rect
}

// Either `superview` and `instructionsRootView` is not in the hierarchy,
// the result is undefined.
guard let superviewWindow = superview.window,
let instructionsWindow = rootView.window else {
print(ErrorMessage.Warning.anchorViewIsNotInTheViewHierarchy)
return rootView.convert(frame, from: superview)
return rootView.convert(rect, from: superview)
}

// If both windows are the same, we can directly convert, because
Expand All @@ -31,21 +38,28 @@ public class CoachMarkCoordinateConverter {
// This is the case when showing Instructions either in the parent
// view controller or the parent window.
guard superviewWindow != instructionsWindow else {
return rootView.convert(frame, from: superview)
return rootView.convert(rect, from: superview)
}

// 1. Converts the coordinates of the frame from its superview to its window.
let frameInWindow = superviewWindow.convert(frame, from: superview)
let rectInWindow = superviewWindow.convert(rect, from: superview)

// 2. Converts the coordinates of the frame from its window to Instructions' window.
let frameInInstructionsWindow = instructionsWindow.convert(frameInWindow,
from: superviewWindow)
let rectInInstructionsWindow = instructionsWindow.convert(rectInWindow,
from: superviewWindow)

// 3. Converts the coordinates of the frame from Instructions' window to
// `instructionsRootView`.
return rootView.convert(frameInInstructionsWindow, from: instructionsWindow)
return rootView.convert(rectInInstructionsWindow, from: instructionsWindow)
}

/// Converts a point from the specified coordinate space
/// to the coordinate space of Instructions.
///
/// - Parameters:
/// - frame: A point in the specified coordinate space.
/// - superview: The coordinate space in which `point` is specified.
/// - Returns: A point specified in the coordinate space of Instructions.
public func convert(point: CGPoint, from superview: UIView?) -> CGPoint {
// No superview, assuming frame in `instructionsRootView`'s coordinate system.
guard let superview = superview else {
Expand Down
47 changes: 10 additions & 37 deletions Sources/Instructions/Helpers/Public/CoachMarkHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -235,47 +235,20 @@ public class CoachMarkHelper {
cutoutPathMaker: cutoutPathMaker)
}

/// Updates the currently stored coach mark with a cutout path set to be
/// around the provided frame in the given view. The cutout path will be
/// slightly larger than the view and have rounded corners, however you can
/// bypass the default creator by providing a block.
///
/// The point of interest (defining where the arrow will sit, horizontally)
/// will be the one provided.
/// Updates the current coach mark, using a configuration block.
///
/// This method is expected to be used in the delegate, after pausing the display.
/// Otherwise, there might not be such a thing as a "current coach mark".
///
/// - Parameter view: the view around which create the cutoutPath
/// - Parameter pointOfInterest: the point of interest toward which the arrow
/// should point
/// - Parameter bezierPathBlock: a block customizing the cutoutPath
@available(
swift,
deprecated: 2.1.0,
message: "Use updateCurrentCoachMark(_:) instead."
)
public func updateCurrentCoachMark(usingFrame frame: CGRect? = nil,
pointOfInterest: CGPoint? = nil,
superview: UIView? = nil,
cutoutPathMaker: CutoutPathMaker? = nil) {
// `currentCoachMark` is inout, so binding it conditionally doesn't
// make sense, we'll have to force unwrap it later anyway since it's
// a value type.
guard flowManager.isPaused, flowManager.currentCoachMark != nil else {
print(ErrorMessage.Error.updateWentWrong)
return
}

update(coachMark: &flowManager.currentCoachMark!,
usingFrame: frame,
pointOfInterest: pointOfInterest,
superview: superview,
cutoutPathMaker: cutoutPathMaker)
}

/// - Parameter configure: A configuration updating the current coach mark.
/// - Parameter coachMark: The coach mark to update.
/// - Parameter frameConverter: Since the cutout path and the point of interest need
/// to be expressed in Instruction's coordinate system, you
/// can used this instance of `CoachMarkCoordinateConverter`
/// to convert rectangles and points.
public func updateCurrentCoachMark(
_ configure: (inout CoachMark, CoachMarkCoordinateConverter) -> Void
_ configure: (_ coachMark: inout CoachMark,
_ frameConverter: CoachMarkCoordinateConverter) -> Void
) {
// `currentCoachMark` is inout, so binding it conditionally doesn't
// make sense, we'll have to force unwrap it later anyway since it's
Expand All @@ -299,7 +272,7 @@ internal extension CoachMarkHelper {
cutoutPathMaker: CutoutPathMaker? = nil
) {
if let frame = frame {
let convertedFrame = coordinateConverter.convert(frame: frame, from: superview)
let convertedFrame = coordinateConverter.convert(rect: frame, from: superview)

let bezierPath: UIBezierPath

Expand Down

0 comments on commit e542487

Please sign in to comment.