Create walkthroughs and guided tours (coach marks) in a simple way, with Swift.

Overview

Instructions

Build status Maintainability Coverage CocoaPods Shield Carthage compatible Join the chat at https://gitter.im/ephread/Instructions

Add customizable coach marks into your iOS project. Available for both iPhone and iPad.

⚠️ Instructions 2.0.1 brings a couple of breaking changes, please review the migration document before updating.

Table of contents

Overview

Instructions Demo

Features

Requirements

  • Xcode 11 / Swift 5+
  • iOS 12.0+

Asking Questions / Contributing

Asking questions

If you need help with something in particular, ask a question in the Gitter room.

Contributing

If you want to contribute, be sure to take a look at the contributing guide.

Installation

CocoaPods

Add Instructions to your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
# Instructions is only supported for iOS 12+, but it
# can be used on older versions at your own risk,
# going as far back as iOS 9.
platform :ios, '9.0'
use_frameworks!

pod 'Instructions', '~> 2.0.1'

Then, run the following command:

$ pod install

Carthage

Add Instructions to your Cartfile:

github "ephread/Instructions" ~> 2.0.1

You can then update, build and drag the generated framework into your project:

$ carthage update
$ carthage build

Swift Package Manager

In Xcode, use File > Swift Packages > Add Package Dependency and use https://github.com/ephread/Instructions.

Manually

If you rather stay away from both CocoaPods and Carthage, you can also install Instructions manually, with the cost of managing updates yourself.

Embedded Framework

  1. Drag the Instructions.xcodeproj into the project navigator of your application's Xcode project.
  2. Still in the project navigator, select your application project. The target configuration panel should show up.
  3. Select the appropriate target and in the "General" panel, scroll down to the section named "Embedded Binaries".
  4. Click on the + button and select the "Instructions.framework" under the "Product" directory.

Usage

Getting started

Open up the controller for which you wish to display coach marks and instantiate a new CoachMarksController. You should also provide a dataSource, which is an object conforming to the CoachMarksControllerDataSource protocol.

class DefaultViewController: UIViewController,
                             CoachMarksControllerDataSource,
                             CoachMarksControllerDelegate {
    let coachMarksController = CoachMarksController()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.coachMarksController.dataSource = self
    }
}

Data Source

CoachMarksControllerDataSource declares three mandatory methods.

The first one asks for the number of coach marks to display. Let's pretend that you want to display only one coach mark. Note that the CoachMarksController requesting the information is supplied, allowing you to supply data for multiple CoachMarksController, within a single dataSource.

func numberOfCoachMarks(for coachMarksController: CoachMarksController) -> Int {
    return 1
}

The second one asks for metadata. This allows you to customize how a coach mark will position and appear, but won't let you define its look (more on this later). Metadata are packaged in a struct named CoachMark. Note the parameter coachMarkAt, it gives you the coach mark logical position, much like and IndexPath would do. coachMarksController provides you with an easy way to create a default CoachMark object, from a given view.

let pointOfInterest = UIView()

func coachMarksController(_ coachMarksController: CoachMarksController,
                          coachMarkAt index: Int) -> CoachMark {
    return coachMarksController.helper.makeCoachMark(for: pointOfInterest)
}

The third one supplies two views (much like cellForRowAtIndexPath) in the form a Tuple. The body view is mandatory, as it's the core of the coach mark. The arrow view is optional.

But for now, lets just return the default views provided by Instructions.

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    coachMarkViewsAt index: Int,
    madeFrom coachMark: CoachMark
) -> (bodyView: UIView & CoachMarkBodyView, arrowView: (UIView & CoachMarkArrowView)?) {
    let coachViews = coachMarksController.helper.makeDefaultCoachViews(
        withArrow: true,
        arrowOrientation: coachMark.arrowOrientation
    )

    coachViews.bodyView.hintLabel.text = "Hello! I'm a Coach Mark!"
    coachViews.bodyView.nextLabel.text = "Ok!"

    return (bodyView: coachViews.bodyView, arrowView: coachViews.arrowView)
}

Starting the coach marks flow

Once the dataSource is set up, you can start displaying the coach marks. You will most likely supply self to start. While the overlay adds itself as a child of the current window (to be on top of everything), the CoachMarksController will add itself as a child of the view controller you provide. That way, the CoachMarksController will receive size change events and react accordingly. Be careful, you can't call start in the viewDidLoad method, since the view hierarchy has to be set up and ready for Instructions to work properly.

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    self.coachMarksController.start(in: .window(over: self))
}

Stopping the coach marks flow

You should always stop the flow, once the view disappear. To avoid animation artefacts and timing issues, don't forget to add the following code to your viewWillDisappear method. Calling stop(immediately: true) will ensure that the flow is stopped immediately upon the disappearance of the view.

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)

    self.coachMarksController.stop(immediately: true)
}

You're all set. For more examples you can check the Examples/ directory provided with the library.

Advanced Usage

Customizing the overlay

You can customize the background color of the overlay using this property:

  • overlay.backgroundColor

You can also make the overlay blur the content sitting behind it. Setting this property to anything else than nil will disable the overlay.backgroundColor:

  • overlay.blurEffectStyle: UIBlurEffectStyle?

You can make the overlay tappable. A tap on the overlay will hide the current coach mark and display the next one.

  • overlay.isUserInteractionEnabled: Bool

You can also allow touch events to be forwarded to the UIView underneath if they happen inside the cutout path…

  • overlay.isUserInteractionEnabledInsideCutoutPath: Bool

…or you can ask the entire overlay to forward touch events to the views under.

  • overlay.areTouchEventsForwarded: Bool

⚠️ The blurring overlay is not supported in app extensions.

Customizing default coach marks

The default coach marks provide minimum customisation options.

Available in both CoachMarkBodyDefaultView and CoachMarkArrowDefaultView:

  • background.innerColor: UIColor: the background color of the coachmark.
  • background.borderColor: UIColor: the border color of the coachmark.
  • background.highlightedInnerColor: UIColor: the background color of the coachmark, when the coach mark is highlighted.
  • background.highlightedBorderColor: UIColor: the border color of the coachmark, when the coach mark is highlighted.

Available only on CoachMarkArrowDefaultView:

  • background.cornerRadius: UIColor: the corner radius of the coach mark.

Note that you can also customize properties on CoachMarkBodyDefaultView.hintLabel and CoachMarkBodyDefaultView.nextLabel.

Refer to MixedCoachMarksViewsViewController.swift for a practical example.

Providing custom views

If the default customisation options are not enough, you can provide your own custom views. A coach mark is composed of two views, a body view and an arrow view. Note that the term arrow might be misleading. It doesn't have to be an actual arrow, it can be anything you want.

A body view must conform to the CoachMarkBodyView protocol. An arrow view must conform to the CoachMarkArrowView protocol. Both of them must also be subclasses of UIView.

Returning a CoachMarkBodyView view is mandatory, while returning a CoachMarkArrowView is optional.

CoachMarkBodyView Protocol

This protocol defines two properties.

  • nextControl: UIControl? { get } you must implement a getter method for this property in your view, this will let the CoachMarkController know which control should be tapped, to display the next coach mark. Note that it doesn't have to be a subview, you can return the view itself.

  • highlightArrowDelegate: CoachMarkBodyHighlightArrowDelegate? in case the view itself is the control receiving taps, you might want to forward its highlight state to the arrow view (so they can look as if they are the same component). The CoachMarkController will automatically set an appropriate delegate to this property. You'll then be able to do this:

override var highlighted: Bool {
    didSet {
        self.highlightArrowDelegate?.highlightArrow(self.highlighted)
    }
}
Taking orientation into account

Remember the following method, from the dataSource?

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    coachMarkViewsAt index: Int,
    madeFrom coachMark: CoachMark
) -> (bodyView: UIView & CoachMarkBodyView, arrowView: (UIView & CoachMarkArrowView)?) {
    let coachViews = coachMarksController.helper.makeDefaultCoachViews(
        withArrow: true,
        arrowOrientation: coachMark.arrowOrientation
    )
}

When providing a customized view, you need to provide an arrow view with the appropriate orientation (i. e. in the case of an actual arrow, pointing upward or downward). The CoachMarkController will tell you which orientation it expects, through the following property: CoachMark.arrowOrientation.

Browse the Example/ directory for more details.

Providing a custom cutout path

If you dislike how the default cutout path looks like, you can customize it by providing a block to makeCoachMark(for:). The cutout path will automatically be stored in the cutoutPath property of the returning CoachMark object:

var coachMark = coachMarksController.helper.makeCoachMark(
    for: customView,
    cutoutPathMaker: { (frame: CGRect) -> UIBezierPath in
        // This will create an oval cutout a bit larger than the view.
        return UIBezierPath(ovalIn: frame.insetBy(dx: -4, dy: -4))
    }
)

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.

Presentation Context

You can choose in which context the coach marks will be displayed, by passing it to `start(in: PresentationContext). The available contexts are:

  • .newWindow(over: UIViewController, at: UIWindowLevel?) – A new window created at the given UIWindowLevel (not available in app extensions);
  • .currentWindow(of: UIViewController) – The window displaying the given UIViewController;
  • .viewController(_: UIViewController) – In the view of the given UIViewController.

Additionally, you can also provide use window(over: UIViewController), which is a convenience static method equivalent to calling .newWindow(over: UIViewController, at: UIWindowLevelNormal + 1).

⚠️ Setting the window level to anything above UIWindowLevelStatusBar is neither supported on iOS 13 nor when using a blur effect on the overlay.

Customizing how the coach mark will show

You can customize the following properties:

  • gapBetweenBodyAndArrow: CGFloat: the vertical gap between the body and the arrow in a given coach mark.

  • pointOfInterest: CGPoint?: the point toward which the arrow will face. At the moment, it's only used to shift the arrow horizontally and make it sits above or below the point of interest.

  • gapBetweenCoachMarkAndCutoutPath: CGFloat: the gap between the coach mark and the cutout path.

  • maxWidth: CGFloat: the maximum width a coach mark can take. You don't want your coach marks to be too wide, especially on iPads.

  • horizontalMargin: CGFloat is the margin (both leading and trailing) between the edges of the overlay view and the coach mark. Note that if the max width of your coach mark is less than the width of the overlay view, you view will either stack on the left or on the right, leaving space on the other side.

  • arrowOrientation: CoachMarkArrowOrientation? is the orientation of the arrow (not the coach mark, meaning setting this property to .Top will display the coach mark below the point of interest). Although it's usually pre-computed by the library, you can override it in coachMarksForIndex: or in coachMarkWillShow:.

  • isDisplayedOverCutoutPath: Bool enables the coach mark to be displayed over the cutout path; please note that arrows won't be visible if you set this property to true

  • isOverlayInteractionEnabled: Bool is used to disable the ability to tap on the overlay to show the next coach mark, on a case-by-case basis; it defaults to true.

  • isUserInteractionEnabledInsideCutoutPath: Bool is used to allow touch forwarding inside the cutout path. Take a look at TransitionFromCodeViewController, in the Example/ directory, for more information.

Animating coach marks

To animates coach marks, you will need to implement the CoachMarksControllerAnimationDelegate protocol.

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    fetchAppearanceTransitionOfCoachMark coachMarkView: UIView,
    at index: Int,
    using manager: CoachMarkTransitionManager
)

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    fetchDisappearanceTransitionOfCoachMark coachMarkView: UIView,
    at index: Int,
    using manager: CoachMarkTransitionManager
)

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    fetchIdleAnimationOfCoachMark coachMarkView: UIView,
    at index: Int,
    using manager: CoachMarkAnimationManager
)

All methods from this delegate work in similar ways. First, you will need to specify the general parameters of the animation via manager.parameters properties. These properties match the configuration parameters that you can provide to UIView.animate.

  • duration: TimeInterval: the total duration of the animation.

  • delay: TimeInterval: the amount of time to wait before beginning the animations

  • options: UIViewAnimationOptions: a mask of options indicating how you want to perform the animations (for regular animations).

  • keyframeOptions: UIViewKeyframeAnimationOptions: a mask of options indicating how you want to perform the animations (for keyframe animations).

Once you've set the parameters, you should provide your animations by calling manager.animate. The method signature is different wether you are animating the idle state of coach marks, or making them appear/disappear.

You should provide your animations in a block passed to the animate parameter, in a similar fashion to UIView.animate. If you need to access the animation parameters or the coach mark metadata, a CoachMarkAnimationManagementContext containing these will be provided to your animation block. You shouldn't capture a reference to manager from the animation block.

For an implementation example, you can also take a look a the DelegateViewController class found in the Example directory.

Appearance and disappearance specifics

If you need to define an initial state, you should do so by providing a block to the fromInitialState property. While directly setting values on coachMarkView in the method before calling manager.animate() might work, it's not guaranteed to.

Let users skip the tour

Control

You can provide the user with a mean to skip the coach marks. First, you will need to set skipView with a UIView conforming to the CoachMarkSkipView protocol. This protocol defines a single property:

public protocol CoachMarkSkipView: AnyObject {
    var skipControl: UIControl? { get }
}

You must implement a getter method for this property in your view. This will let the CoachMarkController know which control should be tapped, to skip the tour. Note that, again, it doesn't have to be a subview, you can return the view itself.

As usual, Instructions provides a default implementation of CoachMarkSkipView named CoachMarkSkipDefaultView.

dataSource

To define how the view will position itself, you can use a method from the CoachMarkControllerDataSource protocol. This method is optional.

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    constraintsForSkipView skipView: UIView,
    inParent parentView: UIView
) -> [NSLayoutConstraint]?

This method will be called by the CoachMarksController before starting the tour and whenever there is a size change. It gives you the skip button and the view in which it will be positioned and expects an array of NSLayoutConstraints in return. These constraints will define how the skip button will be positioned in its parent. You should not add the constraints yourself, just return them.

Returning nil will tell the CoachMarksController to use the defaults constraints, which will position the skip button at the top of the screen. Returning an empty array is discouraged, as it will most probably lead to an awkward positioning.

For more information about the skip mechanism, you can check the Example/ directory.

Piloting the flow from the code

Should you ever need to programmatically show the coach mark, CoachMarkController.flow also provides the following methods:

func showNext(numberOfCoachMarksToSkip numberToSkip: Int = 0)
func showPrevious(numberOfCoachMarksToSkip numberToSkip: Int = 0)

You can specify a number of coach marks to skip (effectively jumping forward or backward to a further index).

Take a look at TransitionFromCodeViewController, in the Example/ directory, to get an idea of how you can leverage this method, in order to ask the user to perform certain actions.

Using a delegate

The CoachMarkController will notify the delegate on multiple occasions. All those methods are optionals.

First, when a coach mark will show. You might want to change something about the view. For that reason, the CoachMark metadata structure is passed as an inout object, so you can update it with new parameters.

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    willShow coachMark: inout CoachMark,
    at index: Int
)

Second, when a coach mark disappears.

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    willHide coachMark: CoachMark,
    at index: Int
)

Third, when all coach marks have been displayed. didEndShowingBySkipping specify whether the flow completed because the user requested it to end.

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    didEndShowingBySkipping skipped: Bool
)
React when the user tap the overlay

Whenever the user will tap the overlay, you will get notified through:

func shouldHandleOverlayTap(
    in coachMarksController: CoachMarksController,
    at index: Int
) -> Bool

Returning true will let Instructions continue the flow normally, while returning false will interrupt it. If you choose to interrupt the flow, you're responsible for either stopping or pausing it or manually show the next coach marks (see Piloting the flow from the code).

index is the index of the coach mark currently displayed.

Pausing and resuming the flow

It's as simple as calling coachMarksController.flow.pause() and coachMarksController.flow.resume(). While pausing, you can also choose to hide Instructions's overlay altogether (.pause(and: hideInstructions)), or only hide the overlay and retain its touch blocking capabilities (.pause(and: hideOverlay)).

Performing animations before showing coach marks

You can perform animation on views, before or after showing a given coach mark. For instance, you might want to collapse a table view and show only its header, before referring to those headers with a coach mark. Instructions offers a simple way to insert your own animations into the flow.

For instance, let's say you want to perform an animation before a coach mark shows. You'll implement some logic into the coachMarkWillShow delegate method. To ensure you don't have to hack something up and turn asynchronous animation blocks into synchronous ones, you can pause the flow, perform the animation and then start the flow again. This will ensure your UI never get stalled.

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    willShow coachMark: inout CoachMark,
    at index: Int
) {
    // Pause to be able to play the animation and then show the coach mark.
    coachMarksController.flow.pause()

    // Run the animation
    UIView.animateWithDuration(1, animations: { () -> Void in
        
    }, completion: { (finished: Bool) -> Void in
        // 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.
        coachMarksController.helper.updateCurrentCoachMarkForView(myView)
        coachMarksController.flow.resume()
    })
}

⚠️ 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 is appearing or disappearing. Otherwise, the animation won't be visible.

You may also want to customize the properties defining the of for the classic transparency overlay, as Instructions will fall back to using the classic type if UIAccessibilityIsReduceTransparencyEnabled() returns true.

Skipping a coach mark

You can skip a given coach mark by implementing the following method defined in CoachMarksControllerDelegate:

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    coachMarkWillLoadAt index: Int
) -> Bool

coachMarkWillLoadAt: is called right before a given coach mark will show. To prevent a CoachMark from showing, you can return false from this method.

Customizing ornaments of the overlay

It's possible to add custom views which will be displayed over the overlay by implementing the following method of CoachMarksControllerDelegate:

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    configureOrnamentsOfOverlay overlay: UIView
)

Just add the ornaments to the provided view (overlay) and Instructions should take care of the rest. Please note, however, that these ornaments will be displayed over the cutout, but under the coach marks.

Dealing with frame changes

Since Instructions doesn't hold any reference to the views of interest, it cannot respond to their change of frame automatically.

Instructions provide two methods to deal with frame changes.

  • CoachMarkController.prepareForChange(), called before a change of frame, to hide the coach mark and the cutout path.
  • CoachMarkController.restoreAfterChangeDidComplete(), called after a change of frame to show the coach mark and the cutout again.

Although you can call these methods at any time while Instructions is idle, the result will not look smooth if the coach mark is already displayed. It's better to perform the changes between two coach marks, by pausing and resuming the flow. KeyboardViewController shows an example of this technique.

Usage within App Extensions

If you wish to add Instructions within App Extensions, there's additional work you need to perform. An example is available in the App Extensions Example/ directory.

Dependencies

Instructions comes with two shared schemes, Instructions and InstructionsAppExtensions. The only differences between the two is that InstructionsAppExtensions does not depend upon the UIApplication.sharedApplication(), making it suitable for App Extensions.

In the following examples, let's consider a project with two targets, one for a regular application (Instructions App Extensions Example) and another for an app extension (Keyboard Extension).

CocoaPods

If you're importing Instructions with CocoaPods, you'll need to edit your Podfile to make it look like this:

target 'Instructions App Extensions Example' do
  pod 'Instructions', '~> 2.0.1'
end

target 'Keyboard Extension' do
  pod 'InstructionsAppExtensions', '~> 2.0.1'
end

If Instructions is only imported from within App Extension target, you don't need the first block.

When compiling either targets, CocoaPods will make sure the appropriate flags are set, thus allowing/forbidding calls to UIApplication.sharedApplication(). You don't need to change your code.

Frameworks (Carthage / Manual management)

If you're importing Instructions through frameworks, you'll notice that the two shared schemes (Instructions and InstructionsAppExtensions) both result in different frameworks.

You need to embed both frameworks and link them to the proper targets. Make sure they look like theses:

Instructions App Extensions Example Imgur

Keyboard Extension Imgur

If you plan to add Instructions only to the App Extension target, you don't need to add Instructions.frameworks.

Import statements

When importing Instructions from files within Instructions App Extensions Example, you should use the regular import statement:

import Instructions

However, when importing Instructions from files within Keyboard Extension, you should use the specific statement:

import InstructionsAppExtensions

⚠️ Please be careful, as you will be able to import regular Instructions from within an app extension without breaking anything. It will work. However, you're at a high risk of rejection from the Apple Store. Uses of UIApplication.sharedApplication() are statically checked during compilation but nothing prevents you from performing the calls at runtime. Fortunately Xcode should warn you if you've mistakenly linked with a framework not suited for App Extensions.

License

Instructions is released under the MIT license. See LICENSE for details.

Comments
  • start(on:) is deprecated, unable to use start(in:)

    start(on:) is deprecated, unable to use start(in:)

    I get a deprecation warning when I use coachMarksController.start(on:) saying to use start(in:)

    Problem is I can not use that function, it is not added to the autocomplete, probably because I can not use PresentationContext: Use of unresolved identifier 'PresentationContext'

    I have added Instructions to my project using cocoa pods as described in your readme.

    bug 
    opened by sandman4sure 21
  • CoachMark is out of bounds if CoachMarkBodyView is created via a XIB.

    CoachMark is out of bounds if CoachMarkBodyView is created via a XIB.

    I am creating a Custom view for the the Body view using xib.

    In delegate function func coachMarksController(_ coachMarksController: CoachMarksController, coachMarkViewsAt index: Int, madeFrom coachMark: CoachMark) -> (bodyView: CoachMarkBodyView, arrowView: CoachMarkArrowView?), I am creating the view from xib and returning that view in bodyView.

    On running my application, I can see that the whole view is out of bounds.

    Please let me know what's causing the issue. Thanks in advance.

    help wanted 
    opened by schinj 16
  • Fix for Mac Catalyst

    Fix for Mac Catalyst

    Hi, I fixed the issue that this repo could not build using Mac Catalyst. To address this issue, I added #if targetEnvironment(macCatalyst) to ignore using [NSLayoutManager hyphenationFactor] that is deprecated in iOS13+.

    opened by 0x0c 15
  • Crash in stopFlow(immediately immediately: Bool = false, userDidSkip skipped: Bool = false, shouldCallDelegate: Bool = true )

    Crash in stopFlow(immediately immediately: Bool = false, userDidSkip skipped: Bool = false, shouldCallDelegate: Bool = true )

    0 libswiftCore.dylib 0x100fb04ac hidden#8277 (_hidden#7745:75) 1 libswiftCore.dylib 0x100fbe3fc swift_unknownUnownedTakeStrong (_hidden#9593:764) 2 Instructions 0x100b1acc8 FlowManager.(stopFlow(immediately : Bool, userDidSkip : Bool, shouldCallDelegate : Bool) -> ()).(closure #2) + 412 3 UIKit 0x1969c9cf8 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 492 4 UIKit 0x1969c981c -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 312 5 UIKit 0x1969c965c -[UIViewAnimationState animationDidStop:finished:] + 160 6 QuartzCore 0x193e4a180 CA::Layer::run_animation_callbacks(void*) + 260 7 libdispatch.dylib 0x18f9c11c0 _dispatch_client_callout + 16 8 libdispatch.dylib 0x18f9c5d6c _dispatch_main_queue_callback_4CF + 1000 9 CoreFoundation 0x190ae3f2c CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 12 10 CoreFoundation 0x190ae1b18 __CFRunLoopRun + 1660 11 CoreFoundation 0x190a10048 CFRunLoopRunSpecific + 444 12 GraphicsServices 0x192496198 GSEventRunModal + 180 13 UIKit 0x1969fc2fc -[UIApplication _run] + 684 14 UIKit 0x1969f7034 UIApplicationMain + 208 15 Shop101 0x1003fdafc main (AppDelegate.swift:31) 16 libdispatch.dylib 0x18f9f45b8 (Missing)

    bug 
    opened by swetaarya 15
  • Crash handling Status Bar Orientation

    Crash handling Status Bar Orientation

    I have noticed a somewhat intermittent crash in our Firebase Crashlytics logs that I'm attempting to reproduce with no success thus far. The crash occurs attempting to show a coach mark anchored to a button on screen load. From the stack trace attached it appears to be trigged handling the device rotation / setting the device orientation. However I have tested rotating the device before, during and after the coach mark is displayed successfully.

    The code in my PopoverController.coachMarksAt method executes return coachMarksController.helper.makeCoachMark(for: mybutton)

    With your understanding of the framework does the attached stack trace point to any particular sequence of events that I can try to reproduce this issue?

    To Reproduce Intermittent

    Environment:

    • Device: iPhones and iPads
    • iOS version: 12.x and 13.x
    • Instructions version: 1.4.0
    • Dependency Manager: Carthage

    Additional Context

    Crashed: com.apple.main-thread
    0  My App                       0x100859e9c PopoverController.coachMarksController(_:coachMarkAt:) + 4309032604 (<compiler-generated>:4309032604)
    1  My App                       0x100859ef0 protocol witness for CoachMarksControllerDataSource.coachMarksController(_:coachMarkAt:) in conformance PopoverController + 4309032688 (<compiler-generated>:4309032688)
    2  Instructions                   0x1012443f4 FlowManager.createAndShowCoachMark(afterResuming:changing:) + 31 (CoachMarksController+Proxy.swift:31)
    3  Instructions                   0x10123374c CoachMarksViewController.restoreAfterChangeDidComplete() + 4320311116 (CoachMarksViewController.swift:4320311116)
    4  Instructions                   0x10123379c @objc CoachMarksViewController.restoreAfterChangeDidComplete() + 4320311196 (<compiler-generated>:4320311196)
    5  Foundation                     0x1ba2e6ffc __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_2 + 28
    6  CoreFoundation                 0x1b9e7499c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 28
    7  CoreFoundation                 0x1b9e749ec ___CFXRegistrationPost1_block_invoke + 68
    8  CoreFoundation                 0x1b9e73ce4 _CFXRegistrationPost1 + 396
    9  CoreFoundation                 0x1b9e7397c ___CFXNotificationPost_block_invoke + 108
    10 CoreFoundation                 0x1b9dec910 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1424
    11 CoreFoundation                 0x1b9e732ac _CFXNotificationPost + 1268
    12 Foundation                     0x1ba1d5b8c -[NSNotificationCenter postNotificationName:object:userInfo:] + 64
    13 UIKitCore                      0x1be0202c8 -[UIApplication _notifyDidChangeStatusBarFrame:] + 272
    14 UIKitCore                      0x1be020874 -[UIApplication setStatusBarOrientation:fromOrientation:windowScene:animationParameters:updateBlock:] + 1404
    15 UIKitCore                      0x1be05f928 __78-[UIWindow _rotateWindowToOrientation:updateStatusBar:duration:skipCallbacks:]_block_invoke + 336
    16 UIKitCore                      0x1bda18504 __58-[_UIWindowRotationAnimationController animateTransition:]_block_invoke_2 + 184
    17 UIKitCore                      0x1be4c8640 +[UIView(Internal) _performBlockDelayingTriggeringResponderEvents:forScene:] + 212
    18 UIKitCore                      0x1bda182ec __58-[_UIWindowRotationAnimationController animateTransition:]_block_invoke + 180
    19 UIKitCore                      0x1be4c44f0 +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 576
    20 UIKitCore                      0x1be4c4ae0 +[UIView(UIViewAnimationWithBlocks) animateWithDuration:delay:options:animations:completion:] + 112
    21 UIKitCore                      0x1bda18188 -[_UIWindowRotationAnimationController animateTransition:] + 484
    22 UIKitCore                      0x1be05cfa4 -[UIWindow _rotateToBounds:withAnimator:transitionContext:] + 624
    23 UIKitCore                      0x1be05f678 -[UIWindow _rotateWindowToOrientation:updateStatusBar:duration:skipCallbacks:] + 1452
    24 UIKitCore                      0x1be05fbb0 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 400
    25 UIKitCore                      0x1be05ef2c -[UIWindow _setRotatableViewOrientation:updateStatusBar:duration:force:] + 132
    26 UIKitCore                      0x1be05de58 __57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 128
    27 UIKitCore                      0x1be05dd5c -[UIWindow _updateToInterfaceOrientation:duration:force:] + 456
    28 Foundation                     0x1ba2e6ffc __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_2 + 28
    29 CoreFoundation                 0x1b9e7499c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 28
    30 CoreFoundation                 0x1b9e749ec ___CFXRegistrationPost1_block_invoke + 68
    31 CoreFoundation                 0x1b9e73ce4 _CFXRegistrationPost1 + 396
    32 CoreFoundation                 0x1b9e7397c ___CFXNotificationPost_block_invoke + 108
    33 CoreFoundation                 0x1b9dec910 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1424
    34 CoreFoundation                 0x1b9e732ac _CFXNotificationPost + 1268
    35 Foundation                     0x1ba1d5b8c -[NSNotificationCenter postNotificationName:object:userInfo:] + 64
    36 UIKitCore                      0x1bdc82560 -[UIDevice setOrientation:animated:] + 256
    37 UIKitCore                      0x1bd7b695c __134-[_UIWindowSceneDeviceOrientationSettingsDiffAction _updateDeviceOrientationWithSettingObserverContext:windowScene:transitionContext:]_block_invoke + 144
    38 UIKitCore                      0x1bdc7f39c ___UISceneSettingsDiffActionPerformChangesWithTransitionContext_block_invoke + 28
    39 UIKitCore                      0x1bdb917bc +[BSAnimationSettings(UIKit) tryAnimatingWithSettings:actions:completion:] + 868
    40 UIKitCore                      0x1bdc7f354 _UISceneSettingsDiffActionPerformChangesWithTransitionContext + 260
    41 UIKitCore                      0x1bd7b688c -[_UIWindowSceneDeviceOrientationSettingsDiffAction _updateDeviceOrientationWithSettingObserverContext:windowScene:transitionContext:] + 252
    42 UIKitCore                      0x1bd7b6780 __163-[_UIWindowSceneDeviceOrientationSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke + 108
    43 UIKitCore                      0x1bdc7f23c _UISceneSettingsDiffActionPerformActionsWithDelayForTransitionContext + 108
    44 UIKitCore                      0x1bd7b6614 -[_UIWindowSceneDeviceOrientationSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:] + 244
    45 UIKitCore                      0x1bd620434 __64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke + 640
    46 UIKitCore                      0x1bd61eef8 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 256
    47 UIKitCore                      0x1bd620164 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 236
    48 UIKitCore                      0x1bdbb3b7c -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:] + 480
    49 FrontBoardServices             0x1bf112918 -[FBSSceneImpl updater:didUpdateSettings:withDiff:transitionContext:completion:] + 560
    50 FrontBoardServices             0x1bf138ff8 __88-[FBSWorkspaceScenesClient sceneID:updateWithSettingsDiff:transitionContext:completion:]_block_invoke_2 + 136
    51 FrontBoardServices             0x1bf11cef4 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 240
    52 FrontBoardServices             0x1bf138f14 __88-[FBSWorkspaceScenesClient sceneID:updateWithSettingsDiff:transitionContext:completion:]_block_invoke + 200
    53 libdispatch.dylib              0x1b9bc1fd8 _dispatch_client_callout + 20
    54 libdispatch.dylib              0x1b9bc4d1c _dispatch_block_invoke_direct + 264
    55 FrontBoardServices             0x1bf15f254 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 48
    56 FrontBoardServices             0x1bf15ef00 -[FBSSerialQueue _queue_performNextIfPossible] + 432
    57 FrontBoardServices             0x1bf15f46c -[FBSSerialQueue _performNextFromRunLoopSource] + 32
    58 CoreFoundation                 0x1b9e98108 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28
    59 CoreFoundation                 0x1b9e9805c __CFRunLoopDoSource0 + 84
    60 CoreFoundation                 0x1b9e977c8 __CFRunLoopDoSources0 + 184
    61 CoreFoundation                 0x1b9e92694 __CFRunLoopRun + 1068
    62 CoreFoundation                 0x1b9e91f40 CFRunLoopRunSpecific + 480
    63 GraphicsServices               0x1c4122534 GSEventRunModal + 108
    64 UIKitCore                      0x1be01d580 UIApplicationMain + 1940
    65 My App                       0x100715570 main + 21 (Action.swift:21)
    66 libdyld.dylib                  0x1b9d10e18 start + 4
    
    opened by chamitha 14
  • Show message-only coachmarks

    Show message-only coachmarks

    Sometimes it is preferable to display just a text, without a next button or a separator. It seems Instructions does not support this at the moment. But I think it would be a good addition.

    opened by kaandedeoglu 14
  • Crashing in CoachMarkDisplayManager

    Crashing in CoachMarkDisplayManager

    Thread : Crashed: com.apple.main-thread 0 Instructions 0x1006d0a58 CoachMarkDisplayManager.(positionCoachMarkView in _E6181A788EC6911C68FBD3A4D57722C9)() -> () (CoachMarkDisplayManager.swift:261) 1 Instructions 0x1006cfbe8 CoachMarkDisplayManager.(prepareCoachMarkForDisplay in _E6181A788EC6911C68FBD3A4D57722C9)() -> () (CoachMarkDisplayManager.swift:154) 2 Instructions 0x1006cf488 CoachMarkDisplayManager.displayCoachMarkView(CoachMarkView, coachMark : CoachMark, completion : () -> ()?) -> () (CoachMarkDisplayManager.swift:91) 3 Instructions 0x1006d4bb8 CoachMarksController.(createAndShowCoachMark in _BC66BB5D8A069CE78924A87F8EC01AC4)(shouldCallDelegate : Bool) -> () (CoachMarksController.swift:530) 4 Instructions 0x1006d8514 CoachMarksController.(startOn(CoachMarksController) -> (UIViewController) -> ()).(closure #2) (CoachMarksController.swift:486) 5 UIKit 0x1878d055c -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 408 6 UIKit 0x1878d00c4 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 188 7 UIKit 0x1878cffcc -[UIViewAnimationState animationDidStop:finished:] + 104 8 QuartzCore 0x1871d962c CA::Layer::run_animation_callbacks(void*) + 296 9 libdispatch.dylib 0x19510d954 _dispatch_client_callout + 16 10 libdispatch.dylib 0x19511220c _dispatch_main_queue_callback_4CF + 1608 11 CoreFoundation 0x182dd3544 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 12 12 CoreFoundation 0x182dd15ec __CFRunLoopRun + 1492 13 CoreFoundation 0x182cfcf74 CFRunLoopRunSpecific + 396 14 GraphicsServices 0x18c75f6fc GSEventRunModal + 168 15 UIKit 0x1878fed94 UIApplicationMain + 1488 16 Tether 0x100030ef8 main (AppDelegate.swift:15) 17 libdyld.dylib 0x19513aa08 start + 4

    bug 
    opened by DiscoverPioneer 12
  • CoachMark misplaced on iPad

    CoachMark misplaced on iPad

    The position of the shown CoachMark is incorrect for iPad (both portrait and landscape) is not correct. The arrow is shown at the centre, where it should be, but the dialog itself is dispositioned: simulator screen shot - ipad pro 10 5-inch - 2018-04-11 at 16 33 57

    You can use the attached demo project for reproduction. Demo.zip

    bug enhancement 
    opened by o15a3d4l11s2 11
  • Make coachmark appear above the view

    Make coachmark appear above the view

    Hi, I'm trying to get the coachmark to appear above the view I'm trying to point to, but am having a hard time figuring out how to do so. I realize that for some views the coachmark automatically appears at the top, but in my case it is not and is being presented off the screen.

    opened by CaptainKurt 11
  • overlay.blurEffectStyle not working

    overlay.blurEffectStyle not working

    I'm trying to add a blur effect to the coachMarksController but it doesn't work, e.g.:

    _coachMarksController.overlay.blurEffectStyle = .prominent

    Results in the background overlay not showing at all and only the text is visible but misplaced on the screen. Any ideas why this wouldn't work?

    bug 
    opened by ciathyza 8
  • Remove Overlay

    Remove Overlay

    Hi there. Is there a way to configure the framework to not add an overlay? I know I can set the overlay background colour to clear visually mimic this but not including it in the first place would be preferred. Thanks!

    enhancement 
    opened by chamitha 8
  • How to handle Skip Action

    How to handle Skip Action

    How to handle skip action.

    If user clip skip, I want to show alert screen -> do you want to skip the Instructions screen? Accept / No -> if click Accept then skip the instructions.

    opened by arishanapalli 0
  • How to adjust skip button

    How to adjust skip button

    How to adjust skip button at bottom center of Overlay View? Since skipView button overlap on notification button. as shown in below image.

        func coachMarkSetup(){
            self.coachMarksController.dataSource = self
            let skipView = CoachMarkSkipDefaultView()
            skipView.titleLabel?.font = UIFont(font: Font.interMedium, size: 14)
            skipView.setTitle("Skip", for: .normal)
            skipView.layer.cornerRadius = 17
            skipView.titleLabel?.layer.cornerRadius = 17
    
            skipView.titleLabel?.textColor =  UIColor.init(named: ColorPalette.gray700Color)
            skipView.backgroundColor =  UIColor.init(named: ColorPalette.gray300Color)
            skipView.layer.backgroundColor = UIColor(named: ColorPalette.gray300Color)?.cgColor
    
            self.coachMarksController.overlay.backgroundColor =  UIColor.init(named: ColorPalette.grayOpacity) ?? .lightGray
            self.coachMarksController.skipView = skipView
            self.coachMarksController.skipView?.backgroundColor = UIColor.init(named: ColorPalette.gray300Color)
            self.coachMarksController.skipView?.translatesAutoresizingMaskIntoConstraints = false
    }
    

    SkipError

    opened by arishanapalli 0
  • Crash - NSLayoutConstraint constant is not finite!

    Crash - NSLayoutConstraint constant is not finite!

    I logged a crash in my firebase console, it happned for one user 3 times and I cant reproduce it 
Here

    Environment

    • Device: iPhone 13 Pro
    • iOS version: 16
    • Instructions version: main
    • Dependency Manager: CocoaPods

    Trace of crash: Fatal Exception: NSInternalInconsistencyException NSLayoutConstraint constant is not finite! That's illegal. constant:inf firstAnchor:<NSLayoutYAxisAnchor:0x283d7fd80 "Instructions.CoachMarkView:0x11ffb0050.bottom"> secondAnchor:<NSLayoutYAxisAnchor:0x283db3840 "Instructions.InstructionsRootView:0x11ffa7ab0.bottom">

    

Fatal Exception: NSInternalInconsistencyException 0 CoreFoundation 0xa248 __exceptionPreprocess 1 libobjc.A.dylib 0x17a68 objc_exception_throw 2 Foundation 0x54681c userInfoForFileAndLine 3 CoreAutoLayout 0xb8e4 -[NSLayoutConstraint _setSymbolicConstant:constant:symbolicConstantMultiplier:] 4 CoreAutoLayout 0x1574 -[NSLayoutConstraint setConstant:] 5 CoreAutoLayout 0x1324 +[NSLayoutConstraint constraintWithAnchor:relatedBy:toAnchor:multiplier:constant:] 6 Instructions 0xfa80 (Manquant UUID e5aa90ad73623213b26ec3ed2d547935) 7 Instructions 0xe868 (Manquant UUID e5aa90ad73623213b26ec3ed2d547935) 8 Instructions 0x16b84 (Manquant UUID e5aa90ad73623213b26ec3ed2d547935) 9 Instructions 0x19f44 (Manquant UUID e5aa90ad73623213b26ec3ed2d547935) 10 Instructions 0x192a0 (Manquant UUID e5aa90ad73623213b26ec3ed2d547935) 11 Instructions 0x166e8 (Manquant UUID e5aa90ad73623213b26ec3ed2d547935) 12 Instructions 0x20bc8 (Manquant UUID e5aa90ad73623213b26ec3ed2d547935) 13 Instructions 0x204e0 (Manquant UUID e5aa90ad73623213b26ec3ed2d547935) 14 UIKitCore 0x10251b0 UIVIEW_IS_EXECUTING_ANIMATION_COMPLETION_BLOCK 15 UIKitCore 0xd2114 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] 16 UIKitCore 0xd1070 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] 17 UIKitCore 0xd0790 -[UIViewAnimationState animationDidStop:finished:] 18 QuartzCore 0x13ae8 CA::Layer::run_animation_callbacks(void*) 19 libdispatch.dylib 0x3fdc dispatch_client_callout 20 libdispatch.dylib 0x127f4 dispatch_main_queue_drain 21 libdispatch.dylib 0x12444 dispatch_main_queue_callback_4CF 22 CoreFoundation 0x9aa08 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE 23 CoreFoundation 0x7c368 __CFRunLoopRun 24 CoreFoundation 0x811e4 CFRunLoopRunSpecific 25 GraphicsServices 0x1368 GSEventRunModal 26 UIKitCore 0x3a2d88 -[UIApplication _run] 27 UIKitCore 0x3a29ec UIApplicationMain 28 libswiftUIKit.dylib 0x352a0 UIApplicationMain(::::) 22 MYAPP 0xdfcc main + 4371423180 (MF.swift:4371423180) 23 ??? 0x1be3bd948 (Manquant)

    opened by haffez23 1
  • Bump nokogiri from 1.13.6 to 1.13.9

    Bump nokogiri from 1.13.6 to 1.13.9

    Bumps nokogiri from 1.13.6 to 1.13.9.

    Release notes

    Sourced from nokogiri's releases.

    1.13.9 / 2022-10-18

    Security

    Dependencies

    • [CRuby] Vendored libxml2 is updated to v2.10.3 from v2.9.14.
    • [CRuby] Vendored libxslt is updated to v1.1.37 from v1.1.35.
    • [CRuby] Vendored zlib is updated from 1.2.12 to 1.2.13. (See LICENSE-DEPENDENCIES.md for details on which packages redistribute this library.)

    Fixed

    • [CRuby] Nokogiri::XML::Namespace objects, when compacted, update their internal struct's reference to the Ruby object wrapper. Previously, with GC compaction enabled, a segmentation fault was possible after compaction was triggered. [#2658] (Thanks, @​eightbitraptor and @​peterzhu2118!)
    • [CRuby] Document#remove_namespaces! now defers freeing the underlying xmlNs struct until the Document is GCed. Previously, maintaining a reference to a Namespace object that was removed in this way could lead to a segfault. [#2658]

    sha256 checksums:

    9b69829561d30c4461ea803baeaf3460e8b145cff7a26ce397119577a4083a02  nokogiri-1.13.9-aarch64-linux.gem
    e76ebb4b7b2e02c72b2d1541289f8b0679fb5984867cf199d89b8ef485764956  nokogiri-1.13.9-arm64-darwin.gem
    15bae7d08bddeaa898d8e3f558723300137c26a2dc2632a1f89c8574c4467165  nokogiri-1.13.9-java.gem
    f6a1dbc7229184357f3129503530af73cc59ceba4932c700a458a561edbe04b9  nokogiri-1.13.9-x64-mingw-ucrt.gem
    36d935d799baa4dc488024f71881ff0bc8b172cecdfc54781169c40ec02cbdb3  nokogiri-1.13.9-x64-mingw32.gem
    ebaf82aa9a11b8fafb67873d19ee48efb565040f04c898cdce8ca0cd53ff1a12  nokogiri-1.13.9-x86-linux.gem
    11789a2a11b28bc028ee111f23311461104d8c4468d5b901ab7536b282504154  nokogiri-1.13.9-x86-mingw32.gem
    01830e1646803ff91c0fe94bc768ff40082c6de8cfa563dafd01b3f7d5f9d795  nokogiri-1.13.9-x86_64-darwin.gem
    8e93b8adec22958013799c8690d81c2cdf8a90b6f6e8150ab22e11895844d781  nokogiri-1.13.9-x86_64-linux.gem
    96f37c1baf0234d3ae54c2c89aef7220d4a8a1b03d2675ff7723565b0a095531  nokogiri-1.13.9.gem
    

    1.13.8 / 2022-07-23

    Deprecated

    • XML::Reader#attribute_nodes is deprecated due to incompatibility between libxml2's xmlReader memory semantics and Ruby's garbage collector. Although this method continues to exist for backwards compatibility, it is unsafe to call and may segfault. This method will be removed in a future version of Nokogiri, and callers should use #attribute_hash instead. [#2598]

    Improvements

    • XML::Reader#attribute_hash is a new method to safely retrieve the attributes of a node from XML::Reader. [#2598, #2599]

    Fixed

    ... (truncated)

    Changelog

    Sourced from nokogiri's changelog.

    1.13.9 / 2022-10-18

    Security

    Dependencies

    • [CRuby] Vendored libxml2 is updated to v2.10.3 from v2.9.14.
    • [CRuby] Vendored libxslt is updated to v1.1.37 from v1.1.35.
    • [CRuby] Vendored zlib is updated from 1.2.12 to 1.2.13. (See LICENSE-DEPENDENCIES.md for details on which packages redistribute this library.)

    Fixed

    • [CRuby] Nokogiri::XML::Namespace objects, when compacted, update their internal struct's reference to the Ruby object wrapper. Previously, with GC compaction enabled, a segmentation fault was possible after compaction was triggered. [#2658] (Thanks, @​eightbitraptor and @​peterzhu2118!)
    • [CRuby] Document#remove_namespaces! now defers freeing the underlying xmlNs struct until the Document is GCed. Previously, maintaining a reference to a Namespace object that was removed in this way could lead to a segfault. [#2658]

    1.13.8 / 2022-07-23

    Deprecated

    • XML::Reader#attribute_nodes is deprecated due to incompatibility between libxml2's xmlReader memory semantics and Ruby's garbage collector. Although this method continues to exist for backwards compatibility, it is unsafe to call and may segfault. This method will be removed in a future version of Nokogiri, and callers should use #attribute_hash instead. [#2598]

    Improvements

    • XML::Reader#attribute_hash is a new method to safely retrieve the attributes of a node from XML::Reader. [#2598, #2599]

    Fixed

    • [CRuby] Calling XML::Reader#attributes is now safe to call. In Nokogiri <= 1.13.7 this method may segfault. [#2598, #2599]

    1.13.7 / 2022-07-12

    Fixed

    XML::Node objects, when compacted, update their internal struct's reference to the Ruby object wrapper. Previously, with GC compaction enabled, a segmentation fault was possible after compaction was triggered. [#2578] (Thanks, @​eightbitraptor!)

    Commits
    • 897759c version bump to v1.13.9
    • aeb1ac3 doc: update CHANGELOG
    • c663e49 Merge pull request #2671 from sparklemotion/flavorjones-update-zlib-1.2.13_v1...
    • 212e07d ext: hack to cross-compile zlib v1.2.13 on darwin
    • 76dbc8c dep: update zlib to v1.2.13
    • 24e3a9c doc: update CHANGELOG
    • 4db3b4d Merge pull request #2668 from sparklemotion/flavorjones-namespace-scopes-comp...
    • 73d73d6 fix: Document#remove_namespaces! use-after-free bug
    • 5f58b34 fix: namespace nodes behave properly when compacted
    • b08a858 test: repro namespace_scopes compaction issue
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Is it possible to add animation (ripple/pulsing) to the coach mark cutout path?

    Is it possible to add animation (ripple/pulsing) to the coach mark cutout path?

    Is it possible to add animation (ripple/pulsing) to the coach mark cutout path?

    https://user-images.githubusercontent.com/51815366/186852528-744d76a5-c283-46e9-8f4b-3b49ba776597.mov

    opened by gowripriya1999 0
  • Remove/Skip all pending CoachMarks

    Remove/Skip all pending CoachMarks

    Basically I want to skip or removed all instructions views which is not displayed yet because another view controller is presented on it before starting the coach & its showing on wrong view controller. So, I want to remove / dismiss all coach marks in this case.

    Thanks.

    opened by hassan-devcrew 0
Owner
Frédéric Maquin
Independent developer focusing on Apple platforms. He/Him.
Frédéric Maquin
Awesome tool for create tutorial walkthrough or coach tour

AwesomeSpotlightView is a nice and simple library for iOS written on Swift 5. It's highly customizable and easy-to-use tool. Works perfectly for tutor

Alexander Shoshiashvili 309 Jan 5, 2023
Simple coach mark library written in Swift

Minamo Simple coach mark library written in Swift Usage Initialize let rippeleView = RippleView() rippeleView.tintColor = UIColor(red: 0.3, green: 0.7

yukiasai 248 Nov 24, 2022
An iOS framework to easily create simple animated walkthrough, written in Swift.

Intro Overview An iOS framework to easily create simple animated walkthrough, written in Swift. Requirements iOS8 Installation with CocoaPods Intro is

Nurdaulet Bolatov 33 Oct 1, 2021
An iOS framework to easily create a beautiful and engaging onboarding experience with only a few lines of code.

Onboard Click Here For More Examples Important Onboard is no longer under active development, and as such if you create any issues or submit pull requ

Mike 6.5k Dec 17, 2022
A swifty iOS framework that allows developers to create beautiful onboarding experiences.

SwiftyOnboard is being sponsored by the following tool; please help to support us by taking a look and signing up to a free trial SwiftyOnboard A simp

Juan Pablo Fernandez 1.2k Jan 2, 2023
✨ An elegant way to guide your beloved users in iOS apps - Material Showcase.

Material Showcase for iOS An elegant and beautiful tap showcase view for iOS apps based on Material Design Guidelines. Requirement iOS 10.0+ Swift 4.2

Aromajoin 349 Dec 5, 2022
A simple and attractive AlertView to onboard your users in your amazing world.

AlertOnboarding A simple and attractive AlertView to onboard your users in your amazing world. PRESENTATION This AlertOnboarding was inspired by this

Boisney Philippe 832 Jan 8, 2023
A simple keyframe-based animation framework for iOS, written in Swift. Perfect for scrolling app intros.

RazzleDazzle is a simple AutoLayout-friendly keyframe animation framework for iOS, written in Swift. Perfect for scrolling app intros. RazzleDazzle gr

IFTTT 3.4k Jan 1, 2023
Swift based simple information view with pointed arrow.

InfoView View to show small text information blocks with arrow pointed to another view.In most cases it will be a button that was pressed. Example To

Anatoliy Voropay 60 Feb 4, 2022
A simple keyframe-based animation framework for UIKit. Perfect for scrolling app intros.

Jazz Hands is a simple keyframe-based animation framework for UIKit. Animations can be controlled via gestures, scroll views, KVO, or ReactiveCocoa. J

IFTTT 6.4k Dec 28, 2022
A super-charged version of MYIntroductionView for building custom app introductions and tutorials.

MYBlurIntroductionView #####NOTICE: As of February 4th, Apple has begun to ban new app submissions using the common blurring method (UIToolbar hack) f

Matthew York 1.5k Dec 23, 2022
Presentation helps you to make tutorials, release notes and animated pages.

Presentation helps you to make tutorials, release notes and animated pages.

HyperRedink 3k Jan 5, 2023
An animated popover that pops out a given frame, great for subtle UI tips and onboarding.

Animated popover that pops out of a frame. You can specify the direction of the popover and the arrow that points to its origin. Color, border radius

Andrea Mazzini 3k Jan 8, 2023
Show overlay and info on app components

SwiftyOverlay App Intro / Instruction component to show data over app UI at run time! Easy to use, Animated and Customizable. Supported Components are

Saeid 80 Aug 29, 2022
OnboardKit - Customizable user onboarding for your UIKit app in Swift

OnboardKit Customizable user onboarding for your UIKit app in Swift Requirements Swift 5.0 Xcode 10 iOS 11.0+ Installation Carthage github "NikolaKire

Nikola Kirev 470 Dec 23, 2022
iOS library Paper Onboarding is a material design UI slider written on Swift.

iOS library Paper Onboarding is a material design UI slider written on Swift. We specialize in the designing and coding of custom UI

Ramotion 3.2k Jan 5, 2023
SwiftyWalkthrough is a library for creating great walkthrough experiences in your apps, written in Swift.

SwiftyWalkthrough is a library for creating great walkthrough experiences in your apps, written in Swift. You can use the library to allow users to navigate and explore your app, step by step, in a predefined way controlled by you.

Rui Costa 370 Nov 24, 2022
A Swift Recreation of Attach-Detach, with some configurable options

Attach-Detach-Sw A Swift Recreation of Attach-Detach, with some configurable options Usage To use, you'll need to specify if you are attaching or deta

Serena 1 Dec 24, 2021
Fully customisable tooltip view in Swift for iOS.

Description EasyTipView is a fully customizable tooltip view written in Swift that can be used as a call to action or informative tip. Contents Featur

Teo 2.9k Dec 27, 2022