diff --git a/DemoViewController/AppDelegate.swift b/Demo/AppDelegate.swift similarity index 100% rename from DemoViewController/AppDelegate.swift rename to Demo/AppDelegate.swift diff --git a/DemoViewController/DemoViewController.swift b/Demo/DemoViewController.swift similarity index 99% rename from DemoViewController/DemoViewController.swift rename to Demo/DemoViewController.swift index dac8cb8..5794446 100644 --- a/DemoViewController/DemoViewController.swift +++ b/Demo/DemoViewController.swift @@ -6,6 +6,8 @@ // Copyright © 2018 None. All rights reserved. // +import HexColors +import RMessage import UIKit class DemoViewController: UIViewController, RMControllerDelegate { diff --git a/DemoViewController/Info.plist b/Demo/Info.plist similarity index 100% rename from DemoViewController/Info.plist rename to Demo/Info.plist diff --git a/DemoViewController/UI Assets/Assets.xcassets/AppIcon.appiconset/Contents.json b/Demo/UI Assets/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from DemoViewController/UI Assets/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Demo/UI Assets/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/DemoViewController/UI Assets/Assets.xcassets/Contents.json b/Demo/UI Assets/Assets.xcassets/Contents.json similarity index 100% rename from DemoViewController/UI Assets/Assets.xcassets/Contents.json rename to Demo/UI Assets/Assets.xcassets/Contents.json diff --git a/RMessage/Assets/Icons.xcassets/TestButtonBackground.imageset/Contents.json b/Demo/UI Assets/Assets.xcassets/TestButtonBackground.imageset/Contents.json similarity index 100% rename from RMessage/Assets/Icons.xcassets/TestButtonBackground.imageset/Contents.json rename to Demo/UI Assets/Assets.xcassets/TestButtonBackground.imageset/Contents.json diff --git a/RMessage/Assets/Icons.xcassets/TestButtonBackground.imageset/NotificationButtonBackground.png b/Demo/UI Assets/Assets.xcassets/TestButtonBackground.imageset/NotificationButtonBackground.png similarity index 100% rename from RMessage/Assets/Icons.xcassets/TestButtonBackground.imageset/NotificationButtonBackground.png rename to Demo/UI Assets/Assets.xcassets/TestButtonBackground.imageset/NotificationButtonBackground.png diff --git a/RMessage/Assets/Icons.xcassets/TestButtonBackground.imageset/NotificationButtonBackground@2x.png b/Demo/UI Assets/Assets.xcassets/TestButtonBackground.imageset/NotificationButtonBackground@2x.png similarity index 100% rename from RMessage/Assets/Icons.xcassets/TestButtonBackground.imageset/NotificationButtonBackground@2x.png rename to Demo/UI Assets/Assets.xcassets/TestButtonBackground.imageset/NotificationButtonBackground@2x.png diff --git a/DemoViewController/UI Assets/Assets.xcassets/doggy.imageset/Contents.json b/Demo/UI Assets/Assets.xcassets/doggy.imageset/Contents.json similarity index 100% rename from DemoViewController/UI Assets/Assets.xcassets/doggy.imageset/Contents.json rename to Demo/UI Assets/Assets.xcassets/doggy.imageset/Contents.json diff --git a/DemoViewController/UI Assets/Assets.xcassets/doggy.imageset/doggy.jpg b/Demo/UI Assets/Assets.xcassets/doggy.imageset/doggy.jpg similarity index 100% rename from DemoViewController/UI Assets/Assets.xcassets/doggy.imageset/doggy.jpg rename to Demo/UI Assets/Assets.xcassets/doggy.imageset/doggy.jpg diff --git a/DemoViewController/UI Assets/Base.lproj/LaunchScreen.storyboard b/Demo/UI Assets/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from DemoViewController/UI Assets/Base.lproj/LaunchScreen.storyboard rename to Demo/UI Assets/Base.lproj/LaunchScreen.storyboard diff --git a/DemoViewController/UI Assets/Main.storyboard b/Demo/UI Assets/Main.storyboard similarity index 100% rename from DemoViewController/UI Assets/Main.storyboard rename to Demo/UI Assets/Main.storyboard diff --git a/DemoViewController/RevealServer.framework/Headers/RevealServer.h b/DemoViewController/RevealServer.framework/Headers/RevealServer.h deleted file mode 100644 index f6296a2..0000000 --- a/DemoViewController/RevealServer.framework/Headers/RevealServer.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// RevealServer.h -// RevealServer -// -// Created by Tony Arnold on 25/11/2015. -// Copyright © 2015 Itty Bitty Apps, Pty Ltd. All rights reserved. -// - -#import - -//! Project version number for RevealServer. -FOUNDATION_EXPORT double RevealServerVersionNumber; - -//! Project version string for RevealServer. -FOUNDATION_EXPORT const unsigned char RevealServerVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/DemoViewController/RevealServer.framework/Info.plist b/DemoViewController/RevealServer.framework/Info.plist deleted file mode 100644 index 3888c64..0000000 --- a/DemoViewController/RevealServer.framework/Info.plist +++ /dev/null @@ -1,52 +0,0 @@ - - - - - BuildMachineOSBuild - 16G1036 - CFBundleDevelopmentRegion - en - CFBundleExecutable - RevealServer - CFBundleIdentifier - com.ittybittyapps.RevealServer - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - RevealServer - CFBundlePackageType - FMWK - CFBundleShortVersionString - 13 - CFBundleSignature - ???? - CFBundleSupportedPlatforms - - iPhoneOS - - CFBundleVersion - 10035 - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 15A372 - DTPlatformName - iphoneos - DTPlatformVersion - 11.0 - DTSDKBuild - 15A372 - DTSDKName - iphoneos11.0 - DTXcode - 0900 - DTXcodeBuild - 9A235 - MinimumOSVersion - 8.0 - UIDeviceFamily - - 1 - - - diff --git a/DemoViewController/RevealServer.framework/Modules/module.modulemap b/DemoViewController/RevealServer.framework/Modules/module.modulemap deleted file mode 100644 index 8b2d46c..0000000 --- a/DemoViewController/RevealServer.framework/Modules/module.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module RevealServer { - umbrella header "RevealServer.h" - - export * - module * { export * } -} diff --git a/DemoViewController/RevealServer.framework/RevealServer b/DemoViewController/RevealServer.framework/RevealServer deleted file mode 100755 index 84cc03c..0000000 Binary files a/DemoViewController/RevealServer.framework/RevealServer and /dev/null differ diff --git a/DemoViewController/RevealServer.framework/Scripts/copy_and_codesign_revealserver.sh b/DemoViewController/RevealServer.framework/Scripts/copy_and_codesign_revealserver.sh deleted file mode 100755 index 5be5670..0000000 --- a/DemoViewController/RevealServer.framework/Scripts/copy_and_codesign_revealserver.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -set -o errexit -set -o nounset - -# Ensure that we have a valid OTHER_LDFLAGS environment variable -OTHER_LDFLAGS=${OTHER_LDFLAGS:=""} - -# Ensure that we have a valid REVEAL_SERVER_FILENAME environment variable -REVEAL_SERVER_FILENAME=${REVEAL_SERVER_FILENAME:="RevealServer.framework"} - -# Ensure that we have a valid REVEAL_SERVER_PATH environment variable -REVEAL_SERVER_PATH=${REVEAL_SERVER_PATH:="${SRCROOT}/${REVEAL_SERVER_FILENAME}"} - -# The path to copy the framework to -app_frameworks_dir="${CODESIGNING_FOLDER_PATH}/Frameworks" - -copy_library() { - mkdir -p "$app_frameworks_dir" - cp -vRf "$REVEAL_SERVER_PATH" "${app_frameworks_dir}/" -} - -codesign_library() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" ]; then - codesign -fs "${EXPANDED_CODE_SIGN_IDENTITY}" "${app_frameworks_dir}/${REVEAL_SERVER_FILENAME}" - fi -} - -main() { - if [[ $OTHER_LDFLAGS =~ "RevealServer" ]]; then - if [ -e "$REVEAL_SERVER_PATH" ]; then - copy_library - codesign_library - echo "${REVEAL_SERVER_FILENAME} is included in this build, and has been copied to $CODESIGNING_FOLDER_PATH" - else - echo "${REVEAL_SERVER_FILENAME} is not included in this build, as it could not be found at $REVEAL_SERVER_PATH" - fi - else - echo "${REVEAL_SERVER_FILENAME} is not included in this build because RevealServer was not present in the OTHER_LDFLAGS environment variable." - fi -} - -main diff --git a/DemoViewController/RevealServer.framework/_CodeSignature/CodeResources b/DemoViewController/RevealServer.framework/_CodeSignature/CodeResources deleted file mode 100644 index b8e0387..0000000 --- a/DemoViewController/RevealServer.framework/_CodeSignature/CodeResources +++ /dev/null @@ -1,166 +0,0 @@ - - - - - files - - Headers/RevealServer.h - - e2S6Vuf8iJXurblvYWL8e3IMO7E= - - Info.plist - - TC/EWzT+4hkOZ4ByXoWbqOK1jt0= - - Modules/module.modulemap - - EuDEeG1dcC1sd+hIW2SkUAImUg8= - - Scripts/copy_and_codesign_revealserver.sh - - yB2zXTggmRD0rra2Hbpxn2zfCRo= - - - files2 - - Headers/RevealServer.h - - hash - - e2S6Vuf8iJXurblvYWL8e3IMO7E= - - hash2 - - i4zuiS2fsgwsoicYEzHuBx32JYfKW38gkopt/7FdINY= - - - Modules/module.modulemap - - hash - - EuDEeG1dcC1sd+hIW2SkUAImUg8= - - hash2 - - tstqiJpIPr4iEd3MDHClLuTB/ciSC/zNlke1AjfSVuU= - - - Scripts/copy_and_codesign_revealserver.sh - - hash - - yB2zXTggmRD0rra2Hbpxn2zfCRo= - - hash2 - - SL0x5cGSOyS1RcojHFEmFX5IDfon/vO37cFsjSIX7LA= - - - - rules - - ^ - - ^.*\.lproj/ - - optional - - weight - 1000 - - ^.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Base\.lproj/ - - weight - 1010 - - ^version.plist$ - - - rules2 - - .*\.dSYM($|/) - - weight - 11 - - ^ - - weight - 20 - - ^(.*/)?\.DS_Store$ - - omit - - weight - 2000 - - ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ - - nested - - weight - 10 - - ^.* - - ^.*\.lproj/ - - optional - - weight - 1000 - - ^.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Base\.lproj/ - - weight - 1010 - - ^Info\.plist$ - - omit - - weight - 20 - - ^PkgInfo$ - - omit - - weight - 20 - - ^[^/]+$ - - nested - - weight - 10 - - ^embedded\.provisionprofile$ - - weight - 20 - - ^version\.plist$ - - weight - 20 - - - - diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f95a80c --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2018 TouchSix, Inc. Adonis Peralta + +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. diff --git a/README.md b/README.md index 468d843..5c72fd9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,173 @@ -## RMessage 3.0.0 (Swift) +RMessage +========== -This branch holds the next version of RMessage, version 3.0.0. It has been redesigned in Swift to have the main functionalities as the latest version of RMessage 2.3.x. In addition it already holds support for: +[![Twitter: @donileo](https://img.shields.io/badge/contact-@donileo-blue.svg?style=flat)](https://twitter.com/donileo) +[![Version](https://img.shields.io/cocoapods/v/RMessage.svg?style=flat)](http://cocoadocs.org/docsets/RMessage) +[![License](https://img.shields.io/cocoapods/l/RMessage.svg?style=flat)](http://cocoadocs.org/docsets/RMessage) +[![Platform](https://img.shields.io/cocoapods/p/RMessage.svg?style=flat)](http://cocoadocs.org/docsets/RMessage) -* Attributed Strings -* Message notification designs in code (No longer via a json design file) -* The passing of generalized UIViews to setup as Left/Right/Background views. -* New message duration types such as messages that can only be dismissed by tapping, or swiping, or both. -* Support for Custom animator objects via the RMessageAnimator Protocol -* More to come... +## Screenshots -### NOT READY FOR PRODUCTION USE ---- -This branch is undergoing heavy development and breaking changes may be added at any time. +![ErrorUnder](Screenshots/ErrorUnder.png) + +![SuccessUnder](Screenshots/SuccessUnder.png) + +![ErrorOver](Screenshots/ErrorOver.png) + +![WarningOver](Screenshots/WarningOver.png) + +# Intro +Welcome to RMessage! RMessage is a simple notification library written in Swift to help you display notification on the screen. Many many customization options are available such as what position of the screen to present in, what colors should be applied to the title, body, background, etc, etc. There are many customizable properties and they can be viewed at the RMessageSpec protocol definition in the [RMessageSpec](https://github.com/donileo/RMessage/blob/master/Sources/RMessage/RMessageSpec.swift) file. + +Get in contact with the developer on Twitter: [donileo](https://twitter.com/donileo) (Adonis Peralta) + +# 3.0.0 Release + +RMessage 3.0.0 is finally here with many new features from 2.x.x: + +* Rewritten in Swift. +* Message notification designs are now easily specified in code via the RMessageSpec protocol - no more passing in design styles via a json design file. +* Support for generic UIViews for the Left/Right/Background of the message. Want to pass a UIButton for the left view or background view? Go ahead. +* Support for Attributed string stylings. +* Support for new message duration types such as messages that can only be dismissed by tapping, or swiping, or both. +* Support for canceling messages already in the queue waiting to be presented. +* Support for Carthage. +* Groundwork for custom animator objects via the RMessageAnimator Protocol has been set, support for passing in your own animators coming soon. + +**Note: RMessage now requires iOS 11.0 and Swift 4.1. So be sure you are ok with not supporting devices below these targets when using RMessage. If you aren't ready to commit to these version of iOS yet feel free to use RMessage 2.x.x until you can.** + +# Installation + +### CocoaPods +For installation via [CocoaPods](https://cocoapods.org/) add the following line to your Podfile: + +``` +pod "RMessage" #This will install the latest +#pod "RMessage", '~> 2.3.0' # This will install RMessage versions of 2.3.x based on ObjC +``` + +### Carthage + +For installation via [Carthage](https://github.com/Carthage/Carthage) add the following line to your Cartfile: + +``` +github "donileo/RMessage" ~> 3.0 +``` + +### Manually +For manual installation copy the all the source files in the Sources folder to your project. + +# Usage + +To show a simple notification using the predefined "errorSpec" styling, animating from the top do the following: + +```swift +// Create an instance of RMController, a controller object which handles the presentation +// of multiples messages on the screen +let rControl = RMController() + +// Tell rControl to present the message +rControl.showMessage( + withSpec: errorSpec, + title: "Your Title", + body: "A description" +) +``` + +To show your own button on the right, an icon (UIImage) on the left and animate from the bottom do this: + +```swift +let rControl = RMController() +rControl.showMessage( + withSpec: normalSpec, + atPosition: .bottom, + title: "Update available", + body: "Please update the app", + leftView: myIconImage, + rightView: myUIButtonHere +) +``` + +To set a default view controller to present in and bypass the "internal presentation view controller detection": + +```swift + let rControl = RMController() + rControl.presentationViewController = myViewController + + ... + + // All messages now presented by this controller will always be on myViewController + rControl.showMessage(....) +``` + +Want to further customize a notification message right before its presented? Do the following: +```swift + let rControl = RMController() + rControl.delegate = self + + ... + + // Now lets implement the RMControllerDelegate customize(message:controller:) method which + // RMController calls right before presenting: + func customize(message: RMessage) { + message.alpha = 0.4 + message.addSubview(aView) + } +``` + +Feel like declaring your own custom message stylings? Simple! + +```swift +let rControl = RMController() + +// Customize the message, by using the DefaultRMessageSpec as your base. +// The DefaultRMessageSpec object is a struct with default values for a +// "default" RMessageSpec styling. All we need to do is pick up the the defaults +// from it and then just specify which variables we want customized. For a list +// of all the possible vars take a look at its definition! +var customSpec = DefaultRMessageSpec() +customSpec.backgroundColor = .red +customSpec.titleColor = .white +customSpec.bodyColor = .white +customSpec.iconImage = UIImage(named: "MyIcon.png") + +// How about a custom spec based on the predefined warningSpec but with +// attributed string attributes for the title and body text? +var attrWarningSpec = warningSpec +attrWarningSpec.titleAttributes = [.backgroundColor: UIColor.red, .foregroundColor: UIColor.white] +attrWarningSpec.bodyAttributes = [ +.backgroundColor: UIColor.blue, .foregroundColor: UIColor.white, +.underlineStyle: NSUnderlineStyle.styleSingle.rawValue, +] + +// Now lets present Tell the RMController to present the message with your custom stylings. +rControl.showMessage( + withSpec: customSpec, + title: "I'm custom", + body: "Nice!" + ) +``` + +Want to present a message that only disappers when tapped? + +```swift +let rControl = RMController() + +var tapOnlySpec = DefaultRMessageSpec() +tapOnlySpec.durationType = .tap + +// Present it +rControl.showMessage( + withSpec: tapOnlySpec, + title: "Tap me Tap me!", + body: "If you don't I wont dismiss!" +) +``` + +Want more examples? Take a look at the [DemoViewController](https://github.com/donileo/RMessage/blob/master/Demo/DemoViewController.swift) file in the RMessage project to see how to use this library. Its very simple. + +# License +RMessage is available under the MIT license. See the LICENSE file for more information. + +# Recent Changes +Can be found in the [releases section](https://github.com/donileo/RMessage/releases) of this repo. diff --git a/RMessage.podspec b/RMessage.podspec new file mode 100644 index 0000000..b7120aa --- /dev/null +++ b/RMessage.podspec @@ -0,0 +1,31 @@ +# +# Be sure to run `pod lib lint RMessage.podspec' to ensure this is a +# valid spec and remove all comments before submitting the spec. +# +# Any lines starting with a # are optional, but encouraged +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# + +Pod::Spec.new do |s| + s.name = "RMessage" + s.version = "3.0.0" + s.license = 'MIT' + s.summary = "Easy to use and customizable messages/notifications for iOS" + s.homepage = "https://github.com/donileo/RMessage" + s.description = <<-DESC +This framework provides an easy to use class to show small customizable notification views on to the screen. +The notification animates on to the screen to your target position, top, bottom, etc and then dismisses according to various options, automatic, on tap, on swipe, never etc. +There are 4 different types already set up for you: Success, Error, Warning, Message, Custom. Give it a try! +DESC + s.author = { "Adonis Peralta" => "donileo@gmail.com" } + s.social_media_url = 'https://twitter.com/donileo' + + s.source = { :git => "https://github.com/donileo/RMessage.git", :tag => s.version.to_s } + s.platform = :ios, '11.0' + s.swift_version = "4.1" + s.requires_arc = true + s.source_files = 'Sources/**/*.{swift}' + s.resources = ['Sources/Resources/**/*.xib', 'Sources/Assets/**/*.{png,jpg,jpeg,json,pdf}'] + s.dependency 'HexColors', '~> 6.0' +end diff --git a/RMessage.xcodeproj/project.pbxproj b/RMessage.xcodeproj/project.pbxproj index fa8246c..ed0ba3d 100644 --- a/RMessage.xcodeproj/project.pbxproj +++ b/RMessage.xcodeproj/project.pbxproj @@ -7,34 +7,47 @@ objects = { /* Begin PBXBuildFile section */ - 7A03FC4221180EB50002EBCF /* RMessage+Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A03FC4121180EB50002EBCF /* RMessage+Components.swift */; }; - 7A2DD593211929E500025ADE /* RMAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2DD592211929E500025ADE /* RMAnimator.swift */; }; - 7A2DD59521192A6B00025ADE /* SlideAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2DD59421192A6B00025ADE /* SlideAnimator.swift */; }; - 7A5EDAC72114401500C2BFE3 /* RMessageSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5EDAC62114401500C2BFE3 /* RMessageSpec.swift */; }; - 7A7AB90C2115191C00A9D5C1 /* UIWindow+ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AB90B2115191C00A9D5C1 /* UIWindow+ViewController.swift */; }; - 7A7F96C7211ECBD600107868 /* RMPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7F96C6211ECBD600107868 /* RMPresenter.swift */; }; - 7A7F96CA211F44AC00107868 /* RMPresenterOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7F96C9211F44AC00107868 /* RMPresenterOptions.swift */; }; - 7A7F96CC211F4F4800107868 /* RMAnimationOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7F96CB211F4F4800107868 /* RMAnimationOptions.swift */; }; + 7A43CAF12144E8A300A2A0D1 /* RMessage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A71D40E2144DA8E0050BD01 /* RMessage.framework */; }; + 7A43CAF22144E8A300A2A0D1 /* RMessage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7A71D40E2144DA8E0050BD01 /* RMessage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7A7045AF21450EFF0086370D /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7045AE21450EFF0086370D /* TestHelpers.swift */; }; + 7A7045B021450EFF0086370D /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7045AE21450EFF0086370D /* TestHelpers.swift */; }; + 7A71D4122144DA8E0050BD01 /* RMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A71D4102144DA8E0050BD01 /* RMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A71D41B2144DBDB0050BD01 /* UIWindow+ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AB90B2115191C00A9D5C1 /* UIWindow+ViewController.swift */; }; + 7A71D41C2144DBDB0050BD01 /* SlideAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2DD59421192A6B00025ADE /* SlideAnimator.swift */; }; + 7A71D41D2144DBDB0050BD01 /* RMessage+Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A03FC4121180EB50002EBCF /* RMessage+Components.swift */; }; + 7A71D41E2144DBDB0050BD01 /* RMessage+Design.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE10D73211805D900543F68 /* RMessage+Design.swift */; }; + 7A71D41F2144DBDB0050BD01 /* RMessageSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5EDAC62114401500C2BFE3 /* RMessageSpec.swift */; }; + 7A71D4202144DBDB0050BD01 /* RMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB3847421138E9900DA8076 /* RMessage.swift */; }; + 7A71D4212144DBDB0050BD01 /* RMAnimationOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7F96CB211F4F4800107868 /* RMAnimationOptions.swift */; }; + 7A71D4222144DBDB0050BD01 /* RMAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2DD592211929E500025ADE /* RMAnimator.swift */; }; + 7A71D4232144DBDB0050BD01 /* RMPresenterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB3DA8121150CFC0061F5B4 /* RMPresenterDelegate.swift */; }; + 7A71D4242144DBDB0050BD01 /* RMPresenter+TouchCompletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE10D7D21180CBF00543F68 /* RMPresenter+TouchCompletion.swift */; }; + 7A71D4252144DBDB0050BD01 /* RMPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7F96C6211ECBD600107868 /* RMPresenter.swift */; }; + 7A71D4262144DBDB0050BD01 /* RMShowOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC67916211CF8F00096B3B4 /* RMShowOperation.swift */; }; + 7A71D4272144DBDB0050BD01 /* RMControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB3847621138F7500DA8076 /* RMControllerDelegate.swift */; }; + 7A71D4282144DBDB0050BD01 /* RMController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB3847221138E8D00DA8076 /* RMController.swift */; }; + 7A71D42D2144E0E30050BD01 /* RMessage.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AB3845E2113887800DA8076 /* RMessage.xib */; }; + 7A71D42E2144E0EA0050BD01 /* Icons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7AB384612113887800DA8076 /* Icons.xcassets */; }; 7AB38424211386C600DA8076 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB38423211386C600DA8076 /* AppDelegate.swift */; }; 7AB38426211386C600DA8076 /* DemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB38425211386C600DA8076 /* DemoViewController.swift */; }; 7AB3842B211386C700DA8076 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7AB3842A211386C700DA8076 /* Assets.xcassets */; }; 7AB3842E211386C700DA8076 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7AB3842C211386C700DA8076 /* LaunchScreen.storyboard */; }; 7AB38439211386C700DA8076 /* RMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB38438211386C700DA8076 /* RMessageTests.swift */; }; - 7AB38444211386C700DA8076 /* RMessageUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB38443211386C700DA8076 /* RMessageUITests.swift */; }; + 7AB38444211386C700DA8076 /* NavigationControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB38443211386C700DA8076 /* NavigationControllerTests.swift */; }; 7AB384562113883700DA8076 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7AB384552113883700DA8076 /* Main.storyboard */; }; - 7AB384622113887900DA8076 /* RMessage.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AB3845E2113887800DA8076 /* RMessage.xib */; }; - 7AB384642113887900DA8076 /* Icons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7AB384612113887800DA8076 /* Icons.xcassets */; }; - 7AB3847321138E8D00DA8076 /* RMController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB3847221138E8D00DA8076 /* RMController.swift */; }; - 7AB3847521138E9900DA8076 /* RMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB3847421138E9900DA8076 /* RMessage.swift */; }; - 7AB3847721138F7500DA8076 /* RMControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB3847621138F7500DA8076 /* RMControllerDelegate.swift */; }; - 7AB3DA8221150CFC0061F5B4 /* RMPresenterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB3DA8121150CFC0061F5B4 /* RMPresenterDelegate.swift */; }; - 7AC67917211CF8F00096B3B4 /* RMShowOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC67916211CF8F00096B3B4 /* RMShowOperation.swift */; }; - 7AE10D74211805D900543F68 /* RMessage+Design.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE10D73211805D900543F68 /* RMessage+Design.swift */; }; - 7AE10D7E21180CBF00543F68 /* RMPresenter+TouchCompletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE10D7D21180CBF00543F68 /* RMPresenter+TouchCompletion.swift */; }; - 7AFB50E3211842D700137DB8 /* HexColors.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A5EDAC2211432FD00C2BFE3 /* HexColors.framework */; }; + 7ADB44922144EAD1009C5782 /* HexColors.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A8E69052144E9C9007D0F50 /* HexColors.framework */; }; + 7ADB44932144EAF1009C5782 /* HexColors.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A8E69052144E9C9007D0F50 /* HexColors.framework */; }; + 7ADB44952144EFA9009C5782 /* ViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADB44942144EFA9009C5782 /* ViewControllerTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 7A43CAF32144E8A300A2A0D1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7AB38418211386C600DA8076 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7A71D40D2144DA8E0050BD01; + remoteInfo = RMessage; + }; 7AB38435211386C700DA8076 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7AB38418211386C600DA8076 /* Project object */; @@ -51,16 +64,33 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 7A43CAF52144E8A300A2A0D1 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 7A43CAF22144E8A300A2A0D1 /* RMessage.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 7A03FC4121180EB50002EBCF /* RMessage+Components.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RMessage+Components.swift"; sourceTree = ""; }; 7A2DD592211929E500025ADE /* RMAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMAnimator.swift; sourceTree = ""; }; 7A2DD59421192A6B00025ADE /* SlideAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideAnimator.swift; sourceTree = ""; }; - 7A5EDAC2211432FD00C2BFE3 /* HexColors.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HexColors.framework; path = Carthage/Build/iOS/HexColors.framework; sourceTree = ""; }; 7A5EDAC62114401500C2BFE3 /* RMessageSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMessageSpec.swift; sourceTree = ""; }; + 7A7045AE21450EFF0086370D /* TestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestHelpers.swift; sourceTree = ""; }; + 7A71D40E2144DA8E0050BD01 /* RMessage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RMessage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A71D4102144DA8E0050BD01 /* RMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RMessage.h; sourceTree = ""; }; + 7A71D4112144DA8E0050BD01 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 7A7AB90B2115191C00A9D5C1 /* UIWindow+ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIWindow+ViewController.swift"; sourceTree = ""; }; 7A7F96C6211ECBD600107868 /* RMPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMPresenter.swift; sourceTree = ""; }; - 7A7F96C9211F44AC00107868 /* RMPresenterOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMPresenterOptions.swift; sourceTree = ""; }; 7A7F96CB211F4F4800107868 /* RMAnimationOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMAnimationOptions.swift; sourceTree = ""; }; + 7A8E69052144E9C9007D0F50 /* HexColors.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HexColors.framework; path = Carthage/Build/iOS/HexColors.framework; sourceTree = ""; }; 7AB38420211386C600DA8076 /* RMessageDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RMessageDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7AB38423211386C600DA8076 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AB38425211386C600DA8076 /* DemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoViewController.swift; sourceTree = ""; }; @@ -71,7 +101,7 @@ 7AB38438211386C700DA8076 /* RMessageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMessageTests.swift; sourceTree = ""; }; 7AB3843A211386C700DA8076 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 7AB3843F211386C700DA8076 /* RMessageUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RMessageUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 7AB38443211386C700DA8076 /* RMessageUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMessageUITests.swift; sourceTree = ""; }; + 7AB38443211386C700DA8076 /* NavigationControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationControllerTests.swift; sourceTree = ""; }; 7AB38445211386C700DA8076 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 7AB384552113883700DA8076 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 7AB3845E2113887800DA8076 /* RMessage.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RMessage.xib; sourceTree = ""; }; @@ -81,16 +111,26 @@ 7AB3847621138F7500DA8076 /* RMControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMControllerDelegate.swift; sourceTree = ""; }; 7AB3DA8121150CFC0061F5B4 /* RMPresenterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMPresenterDelegate.swift; sourceTree = ""; }; 7AC67916211CF8F00096B3B4 /* RMShowOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RMShowOperation.swift; sourceTree = ""; }; + 7ADB44942144EFA9009C5782 /* ViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerTests.swift; sourceTree = ""; }; 7AE10D73211805D900543F68 /* RMessage+Design.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RMessage+Design.swift"; sourceTree = ""; }; 7AE10D7D21180CBF00543F68 /* RMPresenter+TouchCompletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RMPresenter+TouchCompletion.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 7A71D40A2144DA8E0050BD01 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7ADB44922144EAD1009C5782 /* HexColors.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 7AB3841D211386C600DA8076 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7AFB50E3211842D700137DB8 /* HexColors.framework in Frameworks */, + 7ADB44932144EAF1009C5782 /* HexColors.framework in Frameworks */, + 7A43CAF12144E8A300A2A0D1 /* RMessage.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -128,12 +168,20 @@ path = Animators; sourceTree = ""; }; - 7A5EDAC1211432FD00C2BFE3 /* Frameworks */ = { + 7A71D40F2144DA8E0050BD01 /* Sources */ = { isa = PBXGroup; children = ( - 7A5EDAC2211432FD00C2BFE3 /* HexColors.framework */, + 7AB3845F2113887800DA8076 /* Assets */, + 7AB3845D2113887800DA8076 /* Resources */, + 7A7AB90A2115191C00A9D5C1 /* General Extensions */, + 7AE10D72211805BE00543F68 /* RMessage */, + 7A7F96CD211F93DE00107868 /* RMAnimator */, + 7A7F96C8211F35E900107868 /* RMPresenter */, + 7AC67915211CF8B30096B3B4 /* RMController */, + 7A71D4102144DA8E0050BD01 /* RMessage.h */, + 7A71D4112144DA8E0050BD01 /* Info.plist */, ); - name = Frameworks; + path = Sources; sourceTree = ""; }; 7A7AB90A2115191C00A9D5C1 /* General Extensions */ = { @@ -147,7 +195,6 @@ 7A7F96C8211F35E900107868 /* RMPresenter */ = { isa = PBXGroup; children = ( - 7A7F96C9211F44AC00107868 /* RMPresenterOptions.swift */, 7AB3DA8121150CFC0061F5B4 /* RMPresenterDelegate.swift */, 7AE10D7D21180CBF00543F68 /* RMPresenter+TouchCompletion.swift */, 7A7F96C6211ECBD600107868 /* RMPresenter.swift */, @@ -164,15 +211,23 @@ path = RMAnimator; sourceTree = ""; }; + 7A8E69042144E9C9007D0F50 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 7A8E69052144E9C9007D0F50 /* HexColors.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 7AB38417211386C600DA8076 = { isa = PBXGroup; children = ( - 7AB38422211386C600DA8076 /* DemoViewController */, - 7AB38452211387C600DA8076 /* RMessage */, - 7AB38437211386C700DA8076 /* RMessageTests */, - 7AB38442211386C700DA8076 /* RMessageUITests */, + 7AB38422211386C600DA8076 /* Demo */, + 7AB38437211386C700DA8076 /* Tests */, + 7AB38442211386C700DA8076 /* UITests */, + 7A71D40F2144DA8E0050BD01 /* Sources */, 7AB38421211386C600DA8076 /* Products */, - 7A5EDAC1211432FD00C2BFE3 /* Frameworks */, + 7A8E69042144E9C9007D0F50 /* Frameworks */, ); sourceTree = ""; }; @@ -182,11 +237,12 @@ 7AB38420211386C600DA8076 /* RMessageDemo.app */, 7AB38434211386C700DA8076 /* RMessageTests.xctest */, 7AB3843F211386C700DA8076 /* RMessageUITests.xctest */, + 7A71D40E2144DA8E0050BD01 /* RMessage.framework */, ); name = Products; sourceTree = ""; }; - 7AB38422211386C600DA8076 /* DemoViewController */ = { + 7AB38422211386C600DA8076 /* Demo */ = { isa = PBXGroup; children = ( 7AB38423211386C600DA8076 /* AppDelegate.swift */, @@ -194,25 +250,27 @@ 7AB38451211386EA00DA8076 /* UI Assets */, 7AB3842F211386C700DA8076 /* Info.plist */, ); - path = DemoViewController; + path = Demo; sourceTree = ""; }; - 7AB38437211386C700DA8076 /* RMessageTests */ = { + 7AB38437211386C700DA8076 /* Tests */ = { isa = PBXGroup; children = ( 7AB38438211386C700DA8076 /* RMessageTests.swift */, 7AB3843A211386C700DA8076 /* Info.plist */, ); - path = RMessageTests; + path = Tests; sourceTree = ""; }; - 7AB38442211386C700DA8076 /* RMessageUITests */ = { + 7AB38442211386C700DA8076 /* UITests */ = { isa = PBXGroup; children = ( - 7AB38443211386C700DA8076 /* RMessageUITests.swift */, + 7A7045AE21450EFF0086370D /* TestHelpers.swift */, + 7ADB44942144EFA9009C5782 /* ViewControllerTests.swift */, + 7AB38443211386C700DA8076 /* NavigationControllerTests.swift */, 7AB38445211386C700DA8076 /* Info.plist */, ); - path = RMessageUITests; + path = UITests; sourceTree = ""; }; 7AB38451211386EA00DA8076 /* UI Assets */ = { @@ -225,20 +283,6 @@ path = "UI Assets"; sourceTree = ""; }; - 7AB38452211387C600DA8076 /* RMessage */ = { - isa = PBXGroup; - children = ( - 7A7AB90A2115191C00A9D5C1 /* General Extensions */, - 7AE10D72211805BE00543F68 /* RMessage */, - 7A7F96CD211F93DE00107868 /* RMAnimator */, - 7A7F96C8211F35E900107868 /* RMPresenter */, - 7AC67915211CF8B30096B3B4 /* RMController */, - 7AB3845F2113887800DA8076 /* Assets */, - 7AB3845D2113887800DA8076 /* Resources */, - ); - path = RMessage; - sourceTree = ""; - }; 7AB3845D2113887800DA8076 /* Resources */ = { isa = PBXGroup; children = ( @@ -278,7 +322,37 @@ }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 7A71D40B2144DA8E0050BD01 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A71D4122144DA8E0050BD01 /* RMessage.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ + 7A71D40D2144DA8E0050BD01 /* RMessage */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7A71D4172144DA8E0050BD01 /* Build configuration list for PBXNativeTarget "RMessage" */; + buildPhases = ( + 7A71D4092144DA8E0050BD01 /* Sources */, + 7A71D40A2144DA8E0050BD01 /* Frameworks */, + 7A71D40B2144DA8E0050BD01 /* Headers */, + 7A71D40C2144DA8E0050BD01 /* Resources */, + 7A43CAF62144E8EF00A2A0D1 /* Carthage: Copy-Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RMessage; + productName = RMessage; + productReference = 7A71D40E2144DA8E0050BD01 /* RMessage.framework */; + productType = "com.apple.product-type.framework"; + }; 7AB3841F211386C600DA8076 /* RMessageDemo */ = { isa = PBXNativeTarget; buildConfigurationList = 7AB38448211386C700DA8076 /* Build configuration list for PBXNativeTarget "RMessageDemo" */; @@ -289,10 +363,12 @@ 7A62940D2116C2E900A7323B /* Integrate Reveal Server */, 7A5EDAC42114330300C2BFE3 /* Carthage: Copy-Frameworks */, 7A5EDAC52114335500C2BFE3 /* Carthage: Warn about outdated Deps */, + 7A43CAF52144E8A300A2A0D1 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 7A43CAF42144E8A300A2A0D1 /* PBXTargetDependency */, ); name = RMessageDemo; productName = RMessage; @@ -345,6 +421,9 @@ LastUpgradeCheck = 0940; ORGANIZATIONNAME = None; TargetAttributes = { + 7A71D40D2144DA8E0050BD01 = { + CreatedOnToolsVersion = 9.4.1; + }; 7AB3841F211386C600DA8076 = { CreatedOnToolsVersion = 9.4.1; LastSwiftMigration = 0940; @@ -352,7 +431,6 @@ 7AB38433211386C700DA8076 = { CreatedOnToolsVersion = 9.4.1; LastSwiftMigration = 0940; - TestTargetID = 7AB3841F211386C600DA8076; }; 7AB3843E211386C700DA8076 = { CreatedOnToolsVersion = 9.4.1; @@ -377,11 +455,21 @@ 7AB3841F211386C600DA8076 /* RMessageDemo */, 7AB38433211386C700DA8076 /* RMessageTests */, 7AB3843E211386C700DA8076 /* RMessageUITests */, + 7A71D40D2144DA8E0050BD01 /* RMessage */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 7A71D40C2144DA8E0050BD01 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A71D42E2144E0EA0050BD01 /* Icons.xcassets in Resources */, + 7A71D42D2144E0E30050BD01 /* RMessage.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 7AB3841E211386C600DA8076 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -389,8 +477,6 @@ 7AB384562113883700DA8076 /* Main.storyboard in Resources */, 7AB3842E211386C700DA8076 /* LaunchScreen.storyboard in Resources */, 7AB3842B211386C700DA8076 /* Assets.xcassets in Resources */, - 7AB384642113887900DA8076 /* Icons.xcassets in Resources */, - 7AB384622113887900DA8076 /* RMessage.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -411,6 +497,22 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 7A43CAF62144E8EF00A2A0D1 /* Carthage: Copy-Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/HexColors.framework", + ); + name = "Carthage: Copy-Frameworks"; + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/HexColors.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks"; + }; 7A5EDAC42114330300C2BFE3 /* Carthage: Copy-Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -458,27 +560,33 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 7A71D4092144DA8E0050BD01 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A71D4202144DBDB0050BD01 /* RMessage.swift in Sources */, + 7A71D4262144DBDB0050BD01 /* RMShowOperation.swift in Sources */, + 7A71D4282144DBDB0050BD01 /* RMController.swift in Sources */, + 7A71D41E2144DBDB0050BD01 /* RMessage+Design.swift in Sources */, + 7A71D41C2144DBDB0050BD01 /* SlideAnimator.swift in Sources */, + 7A71D41F2144DBDB0050BD01 /* RMessageSpec.swift in Sources */, + 7A71D4212144DBDB0050BD01 /* RMAnimationOptions.swift in Sources */, + 7A71D4222144DBDB0050BD01 /* RMAnimator.swift in Sources */, + 7A71D41D2144DBDB0050BD01 /* RMessage+Components.swift in Sources */, + 7A71D4242144DBDB0050BD01 /* RMPresenter+TouchCompletion.swift in Sources */, + 7A71D41B2144DBDB0050BD01 /* UIWindow+ViewController.swift in Sources */, + 7A71D4252144DBDB0050BD01 /* RMPresenter.swift in Sources */, + 7A71D4232144DBDB0050BD01 /* RMPresenterDelegate.swift in Sources */, + 7A71D4272144DBDB0050BD01 /* RMControllerDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 7AB3841C211386C600DA8076 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7AB3847321138E8D00DA8076 /* RMController.swift in Sources */, - 7AE10D7E21180CBF00543F68 /* RMPresenter+TouchCompletion.swift in Sources */, - 7A7F96C7211ECBD600107868 /* RMPresenter.swift in Sources */, - 7A2DD593211929E500025ADE /* RMAnimator.swift in Sources */, - 7AB3847721138F7500DA8076 /* RMControllerDelegate.swift in Sources */, 7AB38426211386C600DA8076 /* DemoViewController.swift in Sources */, - 7A2DD59521192A6B00025ADE /* SlideAnimator.swift in Sources */, - 7A7F96CC211F4F4800107868 /* RMAnimationOptions.swift in Sources */, - 7A5EDAC72114401500C2BFE3 /* RMessageSpec.swift in Sources */, - 7A7F96CA211F44AC00107868 /* RMPresenterOptions.swift in Sources */, - 7AB3DA8221150CFC0061F5B4 /* RMPresenterDelegate.swift in Sources */, - 7AB3847521138E9900DA8076 /* RMessage.swift in Sources */, - 7AE10D74211805D900543F68 /* RMessage+Design.swift in Sources */, - 7AC67917211CF8F00096B3B4 /* RMShowOperation.swift in Sources */, 7AB38424211386C600DA8076 /* AppDelegate.swift in Sources */, - 7A03FC4221180EB50002EBCF /* RMessage+Components.swift in Sources */, - 7A7AB90C2115191C00A9D5C1 /* UIWindow+ViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -487,6 +595,7 @@ buildActionMask = 2147483647; files = ( 7AB38439211386C700DA8076 /* RMessageTests.swift in Sources */, + 7A7045AF21450EFF0086370D /* TestHelpers.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -494,13 +603,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7AB38444211386C700DA8076 /* RMessageUITests.swift in Sources */, + 7ADB44952144EFA9009C5782 /* ViewControllerTests.swift in Sources */, + 7AB38444211386C700DA8076 /* NavigationControllerTests.swift in Sources */, + 7A7045B021450EFF0086370D /* TestHelpers.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 7A43CAF42144E8A300A2A0D1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7A71D40D2144DA8E0050BD01 /* RMessage */; + targetProxy = 7A43CAF32144E8A300A2A0D1 /* PBXContainerItemProxy */; + }; 7AB38436211386C700DA8076 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 7AB3841F211386C600DA8076 /* RMessageDemo */; @@ -525,6 +641,74 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 7A71D4182144DA8E0050BD01 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.donileo.RMessage; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 7A71D4192144DA8E0050BD01 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.donileo.RMessage; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 7AB38446211386C700DA8076 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -642,6 +826,7 @@ 7AB38449211386C700DA8076 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Manual; @@ -649,19 +834,15 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", - "$(SRCROOT)/DemoViewController", + "$(PROJECT_DIR)/Demo", ); - INFOPLIST_FILE = DemoViewController/Info.plist; + INFOPLIST_FILE = "$(SRCROOT)/Demo/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - OTHER_LDFLAGS = ( - "-ObjC", - "-weak_framework", - RevealServer, - ); + OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.testdomain.RMessageDemo; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -674,6 +855,7 @@ 7AB3844A211386C700DA8076 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Manual; @@ -681,9 +863,9 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", - "$(SRCROOT)/DemoViewController", + "$(PROJECT_DIR)/Demo", ); - INFOPLIST_FILE = DemoViewController/Info.plist; + INFOPLIST_FILE = "$(SRCROOT)/Demo/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -701,10 +883,10 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RMessageTests/Info.plist; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -712,10 +894,10 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.testdomain.RMessageTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RMessage.app/RMessage"; }; name = Debug; }; @@ -723,10 +905,10 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RMessageTests/Info.plist; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -734,9 +916,9 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.testdomain.RMessageTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RMessage.app/RMessage"; }; name = Release; }; @@ -745,8 +927,9 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RMessageUITests/Info.plist; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = UITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -754,10 +937,12 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.testdomain.RMessageUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = RMessage; + TEST_TARGET_NAME = RMessageDemo; }; name = Debug; }; @@ -766,8 +951,9 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RMessageUITests/Info.plist; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = UITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -775,15 +961,26 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.testdomain.RMessageUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = RMessage; + TEST_TARGET_NAME = RMessageDemo; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 7A71D4172144DA8E0050BD01 /* Build configuration list for PBXNativeTarget "RMessage" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7A71D4182144DA8E0050BD01 /* Debug */, + 7A71D4192144DA8E0050BD01 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 7AB3841B211386C600DA8076 /* Build configuration list for PBXProject "RMessage" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/RMessage.xcodeproj/xcshareddata/xcschemes/RMessage.xcscheme b/RMessage.xcodeproj/xcshareddata/xcschemes/RMessage.xcscheme new file mode 100644 index 0000000..68fbe68 --- /dev/null +++ b/RMessage.xcodeproj/xcshareddata/xcschemes/RMessage.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RMessage/RMAnimator/RMAnimationOptions.swift b/RMessage/RMAnimator/RMAnimationOptions.swift deleted file mode 100644 index 6362190..0000000 --- a/RMessage/RMAnimator/RMAnimationOptions.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// RMAnimationOptions.swift -// RMessageDemo -// -// Created by Adonis Peralta on 8/11/18. -// Copyright © 2018 None. All rights reserved. -// - -import Foundation - -protocol RMAnimationOptions { - var presentationDuration: Double { get set } - var dismissalDuration: Double { get set } -} - -struct RMAnimationOptionsDefault: RMAnimationOptions { - var presentationDuration = 0.5 - var dismissalDuration = 0.3 -} diff --git a/RMessage/RMController/RMControllerDelegate.swift b/RMessage/RMController/RMControllerDelegate.swift deleted file mode 100644 index 3f7c961..0000000 --- a/RMessage/RMController/RMControllerDelegate.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// RMControllerDelegate.swift -// RMessage -// -// Created by Adonis Peralta on 8/2/18. -// Copyright © 2018 None. All rights reserved. -// - -import Foundation -import UIKit - -@objc protocol RMControllerDelegate: class { - /// You can customize the given RMessage, like setting its alpha via (messageOpacity) or adding a subview - @objc optional func customize(message: RMessage) -} diff --git a/RMessage/RMPresenter/RMPresenterDelegate.swift b/RMessage/RMPresenter/RMPresenterDelegate.swift deleted file mode 100644 index 9f02584..0000000 --- a/RMessage/RMPresenter/RMPresenterDelegate.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// RMPresenterDelegate.swift -// RMessage -// -// Created by Adonis Peralta on 8/3/18. -// Copyright © 2018 None. All rights reserved. -// - -import Foundation -import UIKit - -@objc protocol RMPresenterDelegate { - @objc optional func presenterWillPresent(_ presenter: RMPresenter, message: RMessage) - @objc optional func presenterIsPresenting(_ presenter: RMPresenter, message: RMessage) - @objc optional func presenterDidPresent(_ presenter: RMPresenter, message: RMessage) - @objc optional func presenterWillDismiss(_ presenter: RMPresenter, message: RMessage) - @objc optional func presenterIsDismissing(_ presenter: RMPresenter, message: RMessage) - @objc optional func presenterDidDismiss(_ presenter: RMPresenter, message: RMessage) - @objc optional func messageSwiped(forPresenter presenter: RMPresenter, message: RMessage) - @objc optional func messageTapped(forPresenter presenter: RMPresenter, message: RMessage) -} diff --git a/RMessage/RMPresenter/RMPresenterOptions.swift b/RMessage/RMPresenter/RMPresenterOptions.swift deleted file mode 100644 index 9301ca4..0000000 --- a/RMessage/RMPresenter/RMPresenterOptions.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// RMPresenterOptions.swift -// RMessageDemo -// -// Created by Adonis Peralta on 8/11/18. -// Copyright © 2018 None. All rights reserved. -// - -import Foundation - -protocol RMPresenterOptions { - var onScreenTime: Double { get set } - var extraOnScreenTimePerPixel: Double { get set } -} - -struct RMPresenterOptionsDefault: RMPresenterOptions { - var onScreenTime = 1.5 - var extraOnScreenTimePerPixel = 0.04 -} diff --git a/RMessage/RMessage/RMessageSpec.swift b/RMessage/RMessage/RMessageSpec.swift deleted file mode 100644 index 6e98670..0000000 --- a/RMessage/RMessage/RMessageSpec.swift +++ /dev/null @@ -1,103 +0,0 @@ -// -// RMessageSpec.swift -// RMessage -// -// Created by Adonis Peralta on 8/3/18. -// Copyright © 2018 None. All rights reserved. -// - -import Foundation -import UIKit - -protocol RMessageSpec { - var backgroundColor: UIColor { get set } - var targetAlpha: CGFloat { get set } - var cornerRadius: CGFloat { get set } - var verticalOffset: CGFloat { get set } - var iconImage: UIImage? { get set } - var iconImageTintColor: UIColor { get set } - var iconImageRelativeCornerRadius: CGFloat { get set } - var backgroundImage: UIImage? { get set } - var titleAttributes: [NSAttributedStringKey: Any]? { get set } - var titleFont: UIFont { get set } - var titleColor: UIColor { get set } - var titleShadowColor: UIColor { get set } - var titleShadowOffset: CGSize { get set } - var titleTextAlignment: NSTextAlignment { get set } - var bodyAttributes: [NSAttributedStringKey: Any]? { get set } - var bodyFont: UIFont { get set } - var bodyColor: UIColor { get set } - var bodyShadowColor: UIColor { get set } - var bodyShadowOffset: CGSize { get set } - var bodyTextAlignment: NSTextAlignment { get set } - var durationType: RMessageDuration { get set } - var timeToDismiss: TimeInterval { get set } - var blurBackground: Bool { get set } - var titleBodyLabelsSizeToFit: Bool { get set } - var disableSpringAnimationPadding: Bool { get set } -} - -struct DefaultRMessageSpec: RMessageSpec { - var backgroundColor = UIColor.white - var targetAlpha = CGFloat(1) - var cornerRadius = CGFloat(0) - var verticalOffset = CGFloat(0) - var iconImage: UIImage? - var iconImageTintColor = UIColor.clear - var iconImageRelativeCornerRadius = CGFloat(0) - var backgroundImage: UIImage? - var titleAttributes: [NSAttributedStringKey: Any]? - var titleFont = UIFont.boldSystemFont(ofSize: 14) - var titleColor = UIColor.black - var titleShadowColor = UIColor.clear - var titleShadowOffset = CGSize.zero - var titleTextAlignment = NSTextAlignment.left - var bodyAttributes: [NSAttributedStringKey: Any]? - var bodyFont = UIFont.boldSystemFont(ofSize: 12) - var bodyColor = UIColor.black - var bodyShadowColor = UIColor.clear - var bodyShadowOffset = CGSize.zero - var bodyTextAlignment = NSTextAlignment.left - var durationType = RMessageDuration.automatic - var timeToDismiss = TimeInterval(-1.0) - var blurBackground = false - var titleBodyLabelsSizeToFit = false - var disableSpringAnimationPadding = false -} - -// MARK: Implementation for the builtin message types - -let errorSpec: RMessageSpec = { - var errorSpec = DefaultRMessageSpec() - errorSpec.backgroundColor = UIColor("#FF2D55") ?? UIColor.black - errorSpec.titleColor = UIColor.white - errorSpec.bodyColor = UIColor.white - errorSpec.iconImage = UIImage(named: "ErrorMessageIcon.png") - return errorSpec -}() - -let warningSpec: RMessageSpec = { - var warningSpec = DefaultRMessageSpec() - warningSpec.backgroundColor = UIColor("#FFCC00") ?? UIColor.black - warningSpec.titleColor = UIColor("#484638") ?? UIColor.white - warningSpec.bodyColor = warningSpec.titleColor - warningSpec.iconImage = UIImage(named: "WarningMessageIcon.png") - return warningSpec -}() - -let normalSpec: RMessageSpec = { - var normalSpec = DefaultRMessageSpec() - normalSpec.backgroundColor = UIColor("#E8E8E8") ?? UIColor.black - normalSpec.titleColor = UIColor("#727C83") ?? UIColor.white - normalSpec.bodyColor = normalSpec.titleColor - return normalSpec -}() - -let successSpec: RMessageSpec = { - var successSpec = DefaultRMessageSpec() - successSpec.backgroundColor = UIColor("#00C060") ?? UIColor.black - successSpec.titleColor = UIColor.white - successSpec.bodyColor = successSpec.titleColor - successSpec.iconImage = UIImage(named: "SuccessMessageIcon.png") - return successSpec -}() diff --git a/RMessageTests/RMessageTests.swift b/RMessageTests/RMessageTests.swift deleted file mode 100644 index 4145aea..0000000 --- a/RMessageTests/RMessageTests.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RMessageTests.swift -// RMessageTests -// -// Created by Adonis Peralta on 8/2/18. -// Copyright © 2018 None. All rights reserved. -// - -@testable import RMessage -import XCTest - -class RMessageTests: XCTestCase { - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. - } - } -} diff --git a/RMessageUITests/RMessageUITests.swift b/RMessageUITests/RMessageUITests.swift deleted file mode 100644 index 2aeeeed..0000000 --- a/RMessageUITests/RMessageUITests.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RMessageUITests.swift -// RMessageUITests -// -// Created by Adonis Peralta on 8/2/18. -// Copyright © 2018 None. All rights reserved. -// - -import XCTest - -class RMessageUITests: XCTestCase { - override func setUp() { - super.setUp() - - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. - XCUIApplication().launch() - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } -} diff --git a/Screenshots/ErrorOver.png b/Screenshots/ErrorOver.png new file mode 100644 index 0000000..c0adfa9 Binary files /dev/null and b/Screenshots/ErrorOver.png differ diff --git a/Screenshots/ErrorUnder.png b/Screenshots/ErrorUnder.png new file mode 100644 index 0000000..e6f1b9a Binary files /dev/null and b/Screenshots/ErrorUnder.png differ diff --git a/Screenshots/SuccessUnder.png b/Screenshots/SuccessUnder.png new file mode 100644 index 0000000..2313679 Binary files /dev/null and b/Screenshots/SuccessUnder.png differ diff --git a/Screenshots/WarningOver.png b/Screenshots/WarningOver.png new file mode 100644 index 0000000..1578249 Binary files /dev/null and b/Screenshots/WarningOver.png differ diff --git a/RMessage/Assets/Icons.xcassets/Contents.json b/Sources/Assets/Icons.xcassets/Contents.json similarity index 100% rename from RMessage/Assets/Icons.xcassets/Contents.json rename to Sources/Assets/Icons.xcassets/Contents.json diff --git a/RMessage/Assets/Icons.xcassets/ErrorMessageIcon.imageset/Contents.json b/Sources/Assets/Icons.xcassets/ErrorMessageIcon.imageset/Contents.json similarity index 100% rename from RMessage/Assets/Icons.xcassets/ErrorMessageIcon.imageset/Contents.json rename to Sources/Assets/Icons.xcassets/ErrorMessageIcon.imageset/Contents.json diff --git a/RMessage/Assets/Icons.xcassets/ErrorMessageIcon.imageset/NotificationBackgroundErrorIcon.png b/Sources/Assets/Icons.xcassets/ErrorMessageIcon.imageset/NotificationBackgroundErrorIcon.png similarity index 100% rename from RMessage/Assets/Icons.xcassets/ErrorMessageIcon.imageset/NotificationBackgroundErrorIcon.png rename to Sources/Assets/Icons.xcassets/ErrorMessageIcon.imageset/NotificationBackgroundErrorIcon.png diff --git a/RMessage/Assets/Icons.xcassets/ErrorMessageIcon.imageset/NotificationBackgroundErrorIcon@2x.png b/Sources/Assets/Icons.xcassets/ErrorMessageIcon.imageset/NotificationBackgroundErrorIcon@2x.png similarity index 100% rename from RMessage/Assets/Icons.xcassets/ErrorMessageIcon.imageset/NotificationBackgroundErrorIcon@2x.png rename to Sources/Assets/Icons.xcassets/ErrorMessageIcon.imageset/NotificationBackgroundErrorIcon@2x.png diff --git a/RMessage/Assets/Icons.xcassets/SuccessMessageIcon.imageset/Contents.json b/Sources/Assets/Icons.xcassets/SuccessMessageIcon.imageset/Contents.json similarity index 100% rename from RMessage/Assets/Icons.xcassets/SuccessMessageIcon.imageset/Contents.json rename to Sources/Assets/Icons.xcassets/SuccessMessageIcon.imageset/Contents.json diff --git a/RMessage/Assets/Icons.xcassets/SuccessMessageIcon.imageset/NotificationBackgroundSuccessIcon.png b/Sources/Assets/Icons.xcassets/SuccessMessageIcon.imageset/NotificationBackgroundSuccessIcon.png similarity index 100% rename from RMessage/Assets/Icons.xcassets/SuccessMessageIcon.imageset/NotificationBackgroundSuccessIcon.png rename to Sources/Assets/Icons.xcassets/SuccessMessageIcon.imageset/NotificationBackgroundSuccessIcon.png diff --git a/RMessage/Assets/Icons.xcassets/SuccessMessageIcon.imageset/NotificationBackgroundSuccessIcon@2x.png b/Sources/Assets/Icons.xcassets/SuccessMessageIcon.imageset/NotificationBackgroundSuccessIcon@2x.png similarity index 100% rename from RMessage/Assets/Icons.xcassets/SuccessMessageIcon.imageset/NotificationBackgroundSuccessIcon@2x.png rename to Sources/Assets/Icons.xcassets/SuccessMessageIcon.imageset/NotificationBackgroundSuccessIcon@2x.png diff --git a/RMessage/Assets/Icons.xcassets/WarningMessageIcon.imageset/Contents.json b/Sources/Assets/Icons.xcassets/WarningMessageIcon.imageset/Contents.json similarity index 100% rename from RMessage/Assets/Icons.xcassets/WarningMessageIcon.imageset/Contents.json rename to Sources/Assets/Icons.xcassets/WarningMessageIcon.imageset/Contents.json diff --git a/RMessage/Assets/Icons.xcassets/WarningMessageIcon.imageset/NotificationBackgroundWarningIcon.png b/Sources/Assets/Icons.xcassets/WarningMessageIcon.imageset/NotificationBackgroundWarningIcon.png similarity index 100% rename from RMessage/Assets/Icons.xcassets/WarningMessageIcon.imageset/NotificationBackgroundWarningIcon.png rename to Sources/Assets/Icons.xcassets/WarningMessageIcon.imageset/NotificationBackgroundWarningIcon.png diff --git a/RMessage/Assets/Icons.xcassets/WarningMessageIcon.imageset/NotificationBackgroundWarningIcon@2x.png b/Sources/Assets/Icons.xcassets/WarningMessageIcon.imageset/NotificationBackgroundWarningIcon@2x.png similarity index 100% rename from RMessage/Assets/Icons.xcassets/WarningMessageIcon.imageset/NotificationBackgroundWarningIcon@2x.png rename to Sources/Assets/Icons.xcassets/WarningMessageIcon.imageset/NotificationBackgroundWarningIcon@2x.png diff --git a/RMessage/General Extensions/UIWindow+ViewController.swift b/Sources/General Extensions/UIWindow+ViewController.swift similarity index 69% rename from RMessage/General Extensions/UIWindow+ViewController.swift rename to Sources/General Extensions/UIWindow+ViewController.swift index c6c8e28..ed82da5 100644 --- a/RMessage/General Extensions/UIWindow+ViewController.swift +++ b/Sources/General Extensions/UIWindow+ViewController.swift @@ -10,9 +10,13 @@ import Foundation import UIKit extension UIWindow { + /// Returns the top view controller in the window going up from the passed in view controller. + /// + /// - Parameter viewController: The view controller from which to start traversing. + /// - Returns: The top view controller in the window. static func topViewController(forViewController viewController: UIViewController) -> UIViewController { - if viewController.presentedViewController != nil { - return topViewController(forViewController: viewController.presentedViewController!) + if viewController.presentingViewController != nil { + return topViewController(forViewController: viewController.presentingViewController!) } else if let navigationController = viewController as? UINavigationController, let visibleVCInNavigationVC = navigationController.visibleViewController { return topViewController(forViewController: visibleVCInNavigationVC) @@ -23,18 +27,13 @@ extension UIWindow { return viewController } + /// Returns the top view controller in the window going up from the root view controller of the window. + /// + /// - Returns: The top view controller in the window. static func topViewController() -> UIViewController? { guard let rootViewController = UIApplication.shared.keyWindow?.rootViewController else { return nil } return topViewController(forViewController: rootViewController) } - - static func defaultViewControllerForPresentation() -> UIViewController { - guard let defaultViewController = UIWindow.topViewController() else { - assert(false, "Key window should always have a root view controller") - return UIViewController() - } - return defaultViewController - } } diff --git a/Sources/Info.plist b/Sources/Info.plist new file mode 100644 index 0000000..2351742 --- /dev/null +++ b/Sources/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.0.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Sources/RMAnimator/RMAnimationOptions.swift b/Sources/RMAnimator/RMAnimationOptions.swift new file mode 100644 index 0000000..ef05666 --- /dev/null +++ b/Sources/RMAnimator/RMAnimationOptions.swift @@ -0,0 +1,22 @@ +// +// RMAnimationOptions.swift +// +// Created by Adonis Peralta on 8/11/18. +// Copyright © 2018 None. All rights reserved. +// + +import Foundation + +public protocol RMAnimationOptions { + /// The duration of the message presentation animation. + var presentationDuration: Double { get set } + + /// The duration of the message dismissal animation. + var dismissalDuration: Double { get set } +} + +/// Default RManimationOptions constructor object. +public struct DefaultRMAnimationOptions: RMAnimationOptions { + public var presentationDuration = 0.5 + public var dismissalDuration = 0.3 +} diff --git a/RMessage/RMAnimator/RMAnimator.swift b/Sources/RMAnimator/RMAnimator.swift similarity index 98% rename from RMessage/RMAnimator/RMAnimator.swift rename to Sources/RMAnimator/RMAnimator.swift index 7c02226..dc1c5b3 100644 --- a/RMessage/RMAnimator/RMAnimator.swift +++ b/Sources/RMAnimator/RMAnimator.swift @@ -1,6 +1,5 @@ // // RMAnimator.swift -// RMessageDemo // // Created by Adonis Peralta on 8/6/18. // Copyright © 2018 None. All rights reserved. @@ -9,7 +8,7 @@ import Foundation import UIKit -@objc protocol RMAnimator { +@objc public protocol RMAnimator { /// The delegate of the animator. var delegate: RMessageAnimatorDelegate? { get set } @@ -40,7 +39,7 @@ import UIKit func dismiss(withCompletion completion: (() -> Void)?) -> Bool } -@objc protocol RMessageAnimatorDelegate { +@objc public protocol RMessageAnimatorDelegate { /// Notifies the animator delegate the animator will soon start to layout the view it manages. /// - Parameter animator: The animator instance doing the layout. @objc optional func animatorWillLayout(_ animator: RMAnimator) diff --git a/RMessage/RMController/RMController.swift b/Sources/RMController/RMController.swift similarity index 52% rename from RMessage/RMController/RMController.swift rename to Sources/RMController/RMController.swift index 780d04b..8d396f8 100644 --- a/RMessage/RMController/RMController.swift +++ b/Sources/RMController/RMController.swift @@ -10,44 +10,43 @@ import Foundation import HexColors import UIKit -class RMController: RMPresenterDelegate { - /** The view controller this message is displayed in */ - lazy var presentationViewController: UIViewController = UIWindow.defaultViewControllerForPresentation() +public class RMController: NSObject, RMPresenterDelegate { + /// The view controller this message is displayed in. + public lazy var presentationViewController: UIViewController? = UIWindow.topViewController() - private let queue: OperationQueue - - /// By setting this delegate it's possible to set a custom offset for the message - weak var delegate: RMControllerDelegate? + /// Delegate of the RMController object. + public weak var delegate: RMControllerDelegate? // Protect access to this variable from data races private(set) var messageOnScreen = false - init() { + private let queue: OperationQueue + + public var queueCount: Int { + return queue.operationCount + } + + public override init() { queue = OperationQueue() // Make it a serial queue queue.maxConcurrentOperationCount = 1 } - /** - Shows a notification message in a specific view controller - @param viewController The view controller to show the notification in. - @param title The title of the message view - @param subtitle The message that is displayed underneath the title (optional) - @param iconImage A custom icon image (optional) - @param type The message type (Message, Warning, Error, Success, Custom) - @param customTypeName The string identifier/key for the custom style to use from specified custom - design file. Only use when specifying an additional custom design file and when the type parameter in this call is - RMessageTypeCustom - @param duration The duration of the notification being displayed - @param callback The block that should be executed, when the user tapped on the message - @param presentingCompletionCallback The block that should be executed on presentation of the message - @param dismissCompletionCallback The block that should be executed on dismissal of the message - @param buttonTitle The title for button (optional) - @param buttonCallback The block that should be executed, when the user tapped on the button - @param messagePosition The position of the message on the screen - @param dismissingEnabled Should the message be dismissed when the user taps/swipes it - */ - func showMessage( + /// Shows a notification message. + /// + /// - Parameters: + /// - spec: A message spec to use for styling the message, please see *RMessageSpec for usage details. + /// - targetPosition: The position *to* which the message should be presented, (default = top). + /// - title: The title text of the message. + /// - body: The body text of the message. + /// - viewController: The view controller in which to present the message (optional). + /// - leftView: A view to show as the left view of the message. + /// - rightView: A view to show as the right view of the message. + /// - backgroundView: A view to show as the background view of the message. + /// - tapCompletion: A callback to be called when the message is tapped. + /// - presentCompletion: A callback to be called when the message is presented. + /// - dismissCompletion: A callback to be called when the message is dismissed. + public func showMessage( withSpec spec: RMessageSpec, atPosition targetPosition: RMessagePosition = .top, title: String, body: String? = nil, viewController: UIViewController? = nil, leftView: UIView? = nil, rightView: UIView? = nil, backgroundView: UIView? = nil, @@ -61,28 +60,31 @@ class RMController: RMPresenterDelegate { return } - let presentOpts = RMPresenterOptionsDefault() - let animOpts = RMAnimationOptionsDefault() + let animOpts = DefaultRMAnimationOptions() - var presentVC: UIViewController + var presentVC: UIViewController? if let viewController = viewController { presentVC = viewController } else { presentVC = presentationViewController } + // Make sure we have a presentation view controller for the message + guard let presentationVC = presentVC else { + return + } + let animator = SlideAnimator( targetPosition: targetPosition, view: message, - superview: presentVC.view, contentView: message.contentView + superview: presentationVC.view, contentView: message.contentView ) let presenter = RMPresenter( message: message, targetPosition: targetPosition, animator: animator, - presentationOptions: presentOpts, animationOptions: animOpts, - tapCompletion: tapCompletion, presentCompletion: presentCompletion, + animationOptions: animOpts, tapCompletion: tapCompletion, presentCompletion: presentCompletion, dismissCompletion: dismissCompletion ) - delegate?.customize?(message: message) + delegate?.customize?(message: message, controller: self) let presentOp = RMShowOperation(message: message, presenter: presenter) queue.addOperation(presentOp) } @@ -93,14 +95,14 @@ class RMController: RMPresenterDelegate { @return YES if the currently displayed notification was successfully dismissed. NO if no notification was currently displayed. */ - func dismissOnScreenMessage(withCompletion completion: (() -> Void)? = nil) -> Bool { + public func dismissOnScreenMessage(withCompletion completion: (() -> Void)? = nil) -> Bool { if let operation = queue.operations.first as? RMShowOperation { operation.presenter.dismiss(withCompletion: completion) } return true } - func cancelPendingDisplayMessages() { + public func cancelPendingDisplayMessages() { queue.cancelAllOperations() } @@ -110,7 +112,7 @@ class RMController: RMPresenterDelegate { Call this method to notify any presenting or on screen messages that the interface has rotated. Ideally should go inside the calling view controllers viewWillTransitionToSize:withTransitionCoordinator: method. */ - func interfaceDidRotate() { + public func interfaceDidRotate() { if let operation = queue.operations.first as? RMShowOperation, operation.presenter.screenStatus == .presenting { operation.presenter.interfaceDidRotate() } diff --git a/Sources/RMController/RMControllerDelegate.swift b/Sources/RMController/RMControllerDelegate.swift new file mode 100644 index 0000000..d390340 --- /dev/null +++ b/Sources/RMController/RMControllerDelegate.swift @@ -0,0 +1,17 @@ +// +// RMControllerDelegate.swift +// RMessage +// +// Created by Adonis Peralta on 8/2/18. +// Copyright © 2018 None. All rights reserved. +// + +import Foundation +import UIKit + +@objc public protocol RMControllerDelegate: class { + /// Implement this function to have a chance in further customizing a message prior to presentation. + /// + /// - Parameter message: The message to customize. + @objc optional func customize(message: RMessage, controller: RMController) +} diff --git a/RMessage/RMController/RMShowOperation.swift b/Sources/RMController/RMShowOperation.swift similarity index 98% rename from RMessage/RMController/RMShowOperation.swift rename to Sources/RMController/RMShowOperation.swift index db48f35..eb0edce 100644 --- a/RMessage/RMController/RMShowOperation.swift +++ b/Sources/RMController/RMShowOperation.swift @@ -1,6 +1,5 @@ // // RMShowOperation.swift -// RMessageDemo // // Created by Adonis Peralta on 8/9/18. // Copyright © 2018 None. All rights reserved. diff --git a/RMessage/RMPresenter/RMPresenter+TouchCompletion.swift b/Sources/RMPresenter/RMPresenter+TouchCompletion.swift similarity index 98% rename from RMessage/RMPresenter/RMPresenter+TouchCompletion.swift rename to Sources/RMPresenter/RMPresenter+TouchCompletion.swift index f4cda7c..f6fef53 100644 --- a/RMessage/RMPresenter/RMPresenter+TouchCompletion.swift +++ b/Sources/RMPresenter/RMPresenter+TouchCompletion.swift @@ -1,6 +1,5 @@ // // RMPresenter+TouchCompletion.swift -// RMessageDemo // // Created by Adonis Peralta on 8/6/18. // Copyright © 2018 None. All rights reserved. diff --git a/RMessage/RMPresenter/RMPresenter.swift b/Sources/RMPresenter/RMPresenter.swift similarity index 83% rename from RMessage/RMPresenter/RMPresenter.swift rename to Sources/RMPresenter/RMPresenter.swift index f780bda..6ee31be 100644 --- a/RMessage/RMPresenter/RMPresenter.swift +++ b/Sources/RMPresenter/RMPresenter.swift @@ -1,6 +1,5 @@ // // RMPresenter.swift -// RMessageDemo // // Created by Adonis Peralta on 8/11/18. // Copyright © 2018 None. All rights reserved. @@ -10,19 +9,18 @@ import Foundation import UIKit @objc class RMPresenter: NSObject, RMessageAnimatorDelegate { + /// Delegate of the presenter object. weak var delegate: RMPresenterDelegate? - private(set) var message: RMessage - var targetPosition: RMessagePosition - - private let animator: RMAnimator - private(set) var presentationOpts: RMPresenterOptions - private(set) var animationOpts: RMAnimationOptions + /// The target position to which the message should be presented. + private(set) var targetPosition: RMessagePosition + /// The amount of time the message will be presented before being dismissed. Only applies when the *durationType* is + /// .automatic. var dimissTime: TimeInterval { switch message.spec.durationType { case .automatic: - return animator.presentationDuration + animator.dismissalDuration + presentationOpts.onScreenTime + Double(message.bounds.size.height) * presentationOpts.extraOnScreenTimePerPixel + return animator.presentationDuration + animator.dismissalDuration + 1.5 + Double(message.bounds.size.height) * 0.04 case .tap, .swipe, .tapSwipe, .endless: return -1 case .timed: @@ -36,7 +34,10 @@ import UIKit /** Did the message already present */ private(set) var didDismiss = false - /// The current presentation status of the message + private(set) var message: RMessage + private(set) var animationOpts: RMAnimationOptions + + /// Value type representing the current presentation status of the message. enum PresentationStatus { /// The message will soon present though is not on screen yet. case willPresent @@ -51,6 +52,7 @@ import UIKit case dismissing } + /// The current screen status of the message. private(set) var screenStatus: PresentationStatus = .willPresent /** Callback block called after the user taps on the message */ @@ -62,23 +64,23 @@ import UIKit /** Callback block called after the message finishes dismissing */ private(set) var dismissCompletion: (() -> Void)? + private let animator: RMAnimator + init( message: RMessage, targetPosition: RMessagePosition, animator: RMAnimator, - presentationOptions presentOpts: RMPresenterOptions, animationOptions animationOpts: RMAnimationOptions, tapCompletion _: (() -> Void)? = nil, presentCompletion _: (() -> Void)? = nil, dismissCompletion _: (() -> Void)? = nil ) { self.message = message self.targetPosition = targetPosition self.animator = animator - presentationOpts = presentOpts self.animationOpts = animationOpts super.init() setupAnimator() setupGestureRecognizers() } - func setupAnimator() { + private func setupAnimator() { animator.delegate = self if let animator = animator as? SlideAnimator { animator.disableAnimationPadding = message.spec.disableSpringAnimationPadding @@ -91,7 +93,9 @@ import UIKit } } - /** Present the message */ + /// Present the message with the animator specified in instantation. + /// + /// - Parameter completion: A callback to be called on completion of the presentation. func present(withCompletion completion: (() -> Void)? = nil) { guard animator.present(withCompletion: completion) else { return @@ -101,6 +105,9 @@ import UIKit } } + /// Dismiss the message with the animator specified in instantation. + /// + /// - Parameter completion: A callback to be called on completion of the dismissal. func dismiss(withCompletion completion: (() -> Void)? = nil) { guard animator.dismiss(withCompletion: completion) else { return @@ -118,6 +125,8 @@ import UIKit // MARK: - Respond to window events + /// Call this method to have the presenter perform any required changes necessary to account for the interface + /// being rotated. func interfaceDidRotate() { guard screenStatus == .presenting && (message.spec.durationType == .automatic || message.spec.durationType == .timed) else { diff --git a/Sources/RMPresenter/RMPresenterDelegate.swift b/Sources/RMPresenter/RMPresenterDelegate.swift new file mode 100644 index 0000000..6965c47 --- /dev/null +++ b/Sources/RMPresenter/RMPresenterDelegate.swift @@ -0,0 +1,68 @@ +// +// RMPresenterDelegate.swift +// RMessage +// +// Created by Adonis Peralta on 8/3/18. +// Copyright © 2018 None. All rights reserved. +// + +import Foundation +import UIKit + +@objc protocol RMPresenterDelegate { + /// Notifies the presenter's delegate that the presenter will soon present the message. + /// + /// - Parameters: + /// - presenter: The presenter presenting the message. + /// - message: The message being presented. + @objc optional func presenterWillPresent(_ presenter: RMPresenter, message: RMessage) + + /// Notifies the presenter's delegate that the presenter is presenting the message. + /// + /// - Parameters: + /// - presenter: The presenter presenting the message. + /// - message: The message being presented. + @objc optional func presenterIsPresenting(_ presenter: RMPresenter, message: RMessage) + + /// Notifies the presenter's delegate that the presenter did present the message. + /// + /// - Parameters: + /// - presenter: The presenter presenting the message. + /// - message: The message being presented. + @objc optional func presenterDidPresent(_ presenter: RMPresenter, message: RMessage) + + /// Notifies the presenter's delegate that the presenter will soon dismiss the message. + /// + /// - Parameters: + /// - presenter: The presenter presenting the message. + /// - message: The message being presented. + @objc optional func presenterWillDismiss(_ presenter: RMPresenter, message: RMessage) + + /// Notifies the presenter's delegate that the presenter is dismissing the message. + /// + /// - Parameters: + /// - presenter: The presenter presenting the message. + /// - message: The message being presented. + @objc optional func presenterIsDismissing(_ presenter: RMPresenter, message: RMessage) + + /// Notifies the presenter's delegate that the presenter did dismiss the message. + /// + /// - Parameters: + /// - presenter: The presenter presenting the message. + /// - message: The message being presented. + @objc optional func presenterDidDismiss(_ presenter: RMPresenter, message: RMessage) + + /// Notifies the presenter's delegate that the message was swiped. + /// + /// - Parameters: + /// - presenter: The presenter presenting the message. + /// - message: The message being presented. + @objc optional func messageSwiped(forPresenter presenter: RMPresenter, message: RMessage) + + /// Notifies the presenter's delegate that the message was tapped. + /// + /// - Parameters: + /// - presenter: The presenter presenting the message. + /// - message: The message being presented. + @objc optional func messageTapped(forPresenter presenter: RMPresenter, message: RMessage) +} diff --git a/Sources/RMPresenter/RMPresenterOptions.swift b/Sources/RMPresenter/RMPresenterOptions.swift new file mode 100644 index 0000000..43bc2a7 --- /dev/null +++ b/Sources/RMPresenter/RMPresenterOptions.swift @@ -0,0 +1,25 @@ +// +// RMPresenterOptions.swift +// RMessageDemo +// +// Created by Adonis Peralta on 8/11/18. +// Copyright © 2018 None. All rights reserved. +// + +import Foundation + +/** Protocol describing options to be passed to presenters of messages for customizing presentation details. + + - NOTE: Presenters are different than animators in that animators simply animate the message's presentation or dismissal, + while presenters handle other details such as *how long* a message stays presented etc. + */ +protocol RMPresenterOptions { + var onScreenTime: Double { get set } + var extraOnScreenTimePerPixel: Double { get set } +} + +/// Default RMPresenterOptions constructor object. +struct DefaultRMPresenterOptions: RMPresenterOptions { + var onScreenTime = 1.5 + var extraOnScreenTimePerPixel = 0.04 +} diff --git a/Sources/RMessage.h b/Sources/RMessage.h new file mode 100644 index 0000000..b97c721 --- /dev/null +++ b/Sources/RMessage.h @@ -0,0 +1,19 @@ +// +// RMessage.h +// RMessage +// +// Created by Adonis Peralta on 9/9/18. +// Copyright © 2018 None. All rights reserved. +// + +#import + +//! Project version number for RMessage. +FOUNDATION_EXPORT double RMessageVersionNumber; + +//! Project version string for RMessage. +FOUNDATION_EXPORT const unsigned char RMessageVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/RMessage/RMessage/Animators/SlideAnimator.swift b/Sources/RMessage/Animators/SlideAnimator.swift similarity index 86% rename from RMessage/RMessage/Animators/SlideAnimator.swift rename to Sources/RMessage/Animators/SlideAnimator.swift index 0963c05..20b9308 100644 --- a/RMessage/RMessage/Animators/SlideAnimator.swift +++ b/Sources/RMessage/Animators/SlideAnimator.swift @@ -1,6 +1,5 @@ // // SlideAnimator.swift -// RMessageDemo // // Created by Adonis Peralta on 8/6/18. // Copyright © 2018 None. All rights reserved. @@ -19,6 +18,7 @@ private enum Constants { /// bottom to target position. This animator handles the layout of its managed view in the managed /// view's superview. class SlideAnimator: NSObject, RMAnimator { + /// Animator delegate. weak var delegate: RMessageAnimatorDelegate? // MARK: - Start the customizable animation properties @@ -95,6 +95,14 @@ class SlideAnimator: NSObject, RMAnimator { addObserver(self, forKeyPath: Constants.KVC.safeAreaInsets, options: [.new], context: &kvcContext) } + /// Ask the animator to present the view with animation. This call may or may not succeed depending on whether + /// the animator was already previously asked to animate or where in the presentation cycle the animator is. + /// In cases when the animator refuses to present this method returns false, otherwise it returns true. + /// + /// - Note: Your implementation of this method must allow for this method to be called multiple times by ignoring + /// subsequent requests. + /// - Parameter completion: A completion closure to execute after presentation is complete. + /// - Returns: A boolean value indicating if the animator executed your instruction to present. func present(withCompletion completion: (() -> Void)?) -> Bool { // Guard against being called under the following conditions: // 1. If currently presenting or dismissing @@ -110,6 +118,14 @@ class SlideAnimator: NSObject, RMAnimator { return true } + /// Ask the animator to dismiss the view with animation. This call may or may not succeed depending on whether + /// the animator was already previously asked to animate or where in the presentation cycle the animator is. + /// In cases when the animator refuses to dismiss this method returns false, otherwise it returns true. + /// + /// - Note: Your implementation of this method must allow for this method to be called multiple times by ignoring + /// subsequent requests. + /// - Parameter completion: A completion closure to execute after presentation is complete. + /// - Returns: A boolean value indicating if the animator executed your instruction to dismiss. func dismiss(withCompletion completion: (() -> Void)?) -> Bool { // Guard against being called under the following conditions: // 1. If currently presenting or dismissing @@ -122,7 +138,7 @@ class SlideAnimator: NSObject, RMAnimator { return true } - func animatePresentation(withCompletion completion: (() -> Void)?) { + private func animatePresentation(withCompletion completion: (() -> Void)?) { assert(view.superview != nil, "view must have superview by this point") isPresenting = true viewTopConstraint.isActive = true @@ -155,7 +171,7 @@ class SlideAnimator: NSObject, RMAnimator { } /** Dismiss the view with a completion block */ - @objc func animateDismissal(withCompletion completion: (() -> Void)?) { + private func animateDismissal(withCompletion completion: (() -> Void)?) { assert(view.superview != nil, "view instance must have a superview by this point!") isDismissing = true DispatchQueue.main.async { @@ -192,7 +208,7 @@ class SlideAnimator: NSObject, RMAnimator { } } - func safeAreaInsetsDidChange(forView view: UIView) { + private func safeAreaInsetsDidChange(forView view: UIView) { var constant = CGFloat(0) if targetPosition == .bottom { constant = springAnimationPadding + view.safeAreaInsets.bottom @@ -224,7 +240,7 @@ class SlideAnimator: NSObject, RMAnimator { calculateSpringAnimationPadding() } - func setupContentViewLayoutGuideConstraint() { + private func setupContentViewLayoutGuideConstraint() { // Install a constraint that guarantees the title subtitle container view is properly spaced from the top layout // guide when animating from top or the bottom layout guide when animating from bottom let safeAreaLayoutGuide = superview.safeAreaLayoutGuide @@ -243,7 +259,7 @@ class SlideAnimator: NSObject, RMAnimator { contentViewSafeAreaGuideConstraint?.priority = UILayoutPriority(rawValue: 749) } - func setupStartingAnimationConstraints() { + private func setupStartingAnimationConstraints() { assert(view.superview != nil, "instance must have a superview by this point") guard let superview = view.superview else { return @@ -257,7 +273,7 @@ class SlideAnimator: NSObject, RMAnimator { viewTopStartConstraint = viewTopConstraint } - func setupFinalAnimationConstraints() { + private func setupFinalAnimationConstraints() { assert(springAnimationPaddingCalculated, "spring animation padding must have been calculated by now!") assert(view.superview != nil, "instance must have a superview by this point") guard let superview = view.superview else { @@ -290,7 +306,7 @@ class SlideAnimator: NSObject, RMAnimator { // Calculate the padding after the view has had a chance to perform its own custom layout changes via the delegate // call to animatorWillLayout, animatorDidLayout - func calculateSpringAnimationPadding() { + private func calculateSpringAnimationPadding() { if disableAnimationPadding { springAnimationPadding = CGFloat(0) springAnimationPaddingCalculated = true diff --git a/RMessage/RMessage/Extensions/RMessage+Components.swift b/Sources/RMessage/Extensions/RMessage+Components.swift similarity index 99% rename from RMessage/RMessage/Extensions/RMessage+Components.swift rename to Sources/RMessage/Extensions/RMessage+Components.swift index 2e7fabf..b3e6322 100644 --- a/RMessage/RMessage/Extensions/RMessage+Components.swift +++ b/Sources/RMessage/Extensions/RMessage+Components.swift @@ -1,6 +1,5 @@ // // RMessage+Components.swift -// RMessageDemo // // Created by Adonis Peralta on 8/6/18. // Copyright © 2018 None. All rights reserved. diff --git a/RMessage/RMessage/Extensions/RMessage+Design.swift b/Sources/RMessage/Extensions/RMessage+Design.swift similarity index 89% rename from RMessage/RMessage/Extensions/RMessage+Design.swift rename to Sources/RMessage/Extensions/RMessage+Design.swift index 593ddbb..c581f41 100644 --- a/RMessage/RMessage/Extensions/RMessage+Design.swift +++ b/Sources/RMessage/Extensions/RMessage+Design.swift @@ -1,6 +1,5 @@ // // RMessage+Design.swift -// RMessageDemo // // Created by Adonis Peralta on 8/6/18. // Copyright © 2018 None. All rights reserved. @@ -23,12 +22,12 @@ extension RMessage { if spec.titleBodyLabelsSizeToFit { setupLabelConstraintsToSizeToFit() } } - fileprivate func setupDesign(messageSpec spec: RMessageSpec) { + private func setupDesign(messageSpec spec: RMessageSpec) { if spec.cornerRadius > 0 { clipsToBounds = true } backgroundColor = spec.backgroundColor } - fileprivate func setupDesign(forTitleLabel titleLabel: UILabel, messageSpec spec: RMessageSpec) { + private func setupDesign(forTitleLabel titleLabel: UILabel, messageSpec spec: RMessageSpec) { titleLabel.numberOfLines = 0 titleLabel.lineBreakMode = .byWordWrapping titleLabel.font = UIFont.boldSystemFont(ofSize: 14) @@ -44,7 +43,7 @@ extension RMessage { titleLabel.shadowOffset = spec.titleShadowOffset } - fileprivate func setupDesign(forBodyLabel bodyLabel: UILabel, messageSpec spec: RMessageSpec) { + private func setupDesign(forBodyLabel bodyLabel: UILabel, messageSpec spec: RMessageSpec) { bodyLabel.numberOfLines = 0 bodyLabel.lineBreakMode = .byWordWrapping bodyLabel.font = UIFont.boldSystemFont(ofSize: 12) @@ -60,7 +59,7 @@ extension RMessage { bodyLabel.shadowOffset = spec.bodyShadowOffset } - fileprivate func setup(attributedTitleLabel titleLabel: UILabel, withAttributes attrs: [NSAttributedStringKey: Any]) { + private func setup(attributedTitleLabel titleLabel: UILabel, withAttributes attrs: [NSAttributedStringKey: Any]) { guard let titleText = titleLabel.text else { return } @@ -68,7 +67,7 @@ extension RMessage { titleLabel.attributedText = titleAttributedText } - fileprivate func setup(attributedBodyLabel bodyLabel: UILabel, withAttributes attrs: [NSAttributedStringKey: Any]) { + private func setup(attributedBodyLabel bodyLabel: UILabel, withAttributes attrs: [NSAttributedStringKey: Any]) { guard let bodyText = bodyLabel.text else { return } diff --git a/RMessage/RMessage/RMessage.swift b/Sources/RMessage/RMessage.swift similarity index 93% rename from RMessage/RMessage/RMessage.swift rename to Sources/RMessage/RMessage.swift index 7c27e64..3335c61 100644 --- a/RMessage/RMessage/RMessage.swift +++ b/Sources/RMessage/RMessage.swift @@ -10,15 +10,7 @@ import Foundation import HexColors import UIKit -enum RMessagePosition { - case top, bottom, navBarOverlay -} - -enum RMessageDuration { - case automatic, endless, tap, swipe, tapSwipe, timed -} - -class RMessage: UIView, RMessageAnimatorDelegate { +public class RMessage: UIView, RMessageAnimatorDelegate { private(set) var spec: RMessageSpec @IBOutlet private(set) var containerView: UIView! @@ -68,7 +60,7 @@ class RMessage: UIView, RMessageAnimatorDelegate { setupDesign(withMessageSpec: spec, titleLabel: titleLabel, bodyLabel: bodyLabel) } - required init?(coder aDecoder: NSCoder) { + public required init?(coder aDecoder: NSCoder) { spec = DefaultRMessageSpec() super.init(coder: aDecoder) } @@ -85,7 +77,7 @@ class RMessage: UIView, RMessageAnimatorDelegate { containerView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true } - func setupComponents(withMessageSpec spec: RMessageSpec) { + private func setupComponents(withMessageSpec spec: RMessageSpec) { if let image = spec.iconImage, leftView == nil { leftView = iconImageView(withImage: image, imageTintColor: spec.iconImageTintColor, superview: self) } @@ -140,7 +132,7 @@ class RMessage: UIView, RMessageAnimatorDelegate { // MARK: - Respond to Layout Changes - override func layoutSubviews() { + public override func layoutSubviews() { super.layoutSubviews() if titleLabel.text == nil || bodyLabel.text == nil { titleBodyVerticalSpacingConstraint.constant = 0 } diff --git a/Sources/RMessage/RMessageSpec.swift b/Sources/RMessage/RMessageSpec.swift new file mode 100644 index 0000000..1f7fe60 --- /dev/null +++ b/Sources/RMessage/RMessageSpec.swift @@ -0,0 +1,188 @@ +// +// RMessageSpec.swift +// RMessage +// +// Created by Adonis Peralta on 8/3/18. +// Copyright © 2018 None. All rights reserved. +// + +import Foundation +import UIKit + +/** A value representing a position *to* which to present the message. + + - **top**: Position describing the top of the window. + - **bottom**: Position describing the bottom of the window. + - **navBarOverlay**: Position describing the top of the window but overlaying any present navigation bars. + */ +public enum RMessagePosition { + case top, bottom, navBarOverlay +} + +/** A value representing how long a message should stay presented. + + - **automatic**: Duration specifying that the message will self dismiss after some short time. + - **endless**: Duration specifying that the message will never dismiss unless programmatically told to. + - **tap**: Duration specifying that the message will dismiss but only when it is tapped. + - **swipe**: Duration specifying that the message will dismiss but only when it is swiped. + - **tapSwipe**: Duration specifying that the message will dismiss but only when it is tapped or swiped. + - **timed**: Duration specifying that the message will dismiss after some duration specified by the *timeToDismiss* + property. + */ +public enum RMessageDuration { + case automatic, endless, tap, swipe, tapSwipe, timed +} + +public protocol RMessageSpec { + /// Background color for the message. + var backgroundColor: UIColor { get set } + + /// The target opacity of the message after it finishes the presentation animation. + var targetAlpha: CGFloat { get set } + + /// The corner radius of the message. + var cornerRadius: CGFloat { get set } + + /// Vertical offset to apply to the message. + /// Use this to vertically shift the messages position after it finishes presenting. + var verticalOffset: CGFloat { get set } + + /// An icon image to display to the left of the message. + var iconImage: UIImage? { get set } + + /// Icon image tint color to apply to icon images defined to be template images. + /// Does nothing for icon images that are not template images. + var iconImageTintColor: UIColor { get set } + + /// Corner radius percentage relative to icon image to apply to icon image. + /// + /// For example 0.5 (use 50% of icon image width as corner radius) would mask the icon image to always be a circle. + var iconImageRelativeCornerRadius: CGFloat { get set } + + /// Background image to use for the message. + var backgroundImage: UIImage? { get set } + + /// Attributes to apply to the title text to style as an attributed string. Styles applied with this property + /// override stylings applied by other properties in the RMessageSpec. + var titleAttributes: [NSAttributedStringKey: Any]? { get set } + + /// Font to apply to the title text. + var titleFont: UIFont { get set } + + /// Color to apply to the title text. + var titleColor: UIColor { get set } + + /// Color to apply to the title shadow. + var titleShadowColor: UIColor { get set } + + /// Offset to apply to the title shadow. + var titleShadowOffset: CGSize { get set } + + /// Text alingment of the title text. + var titleTextAlignment: NSTextAlignment { get set } + + /// Attributes to apply to the body text to style as an attributed string. Styles applied with this property + /// override stylings applied by other properties in the RMessageSpec. + var bodyAttributes: [NSAttributedStringKey: Any]? { get set } + + /// Font to apply to the body text. + var bodyFont: UIFont { get set } + + /// Color to the apply to the body text. + var bodyColor: UIColor { get set } + + /// Color to apply to the body shadow. + var bodyShadowColor: UIColor { get set } + + /// Offset to apply to the body shadow. + var bodyShadowOffset: CGSize { get set } + + /// Text alingment of the body text. + var bodyTextAlignment: NSTextAlignment { get set } + + /// The type of duration of the message. + var durationType: RMessageDuration { get set } + + /// Time in seconds to dismiss the message. Use only in conjuction with a *durationType* of **timed**. + var timeToDismiss: TimeInterval { get set } + + /// A boolean value specifying whether to blur the background of the message. If attempting to blur a background color + // make sure the background color has alpha value less than 1 when enabling this feature. + var blurBackground: Bool { get set } + + /// A boolean value specifying whether to size the title and body labels to fit their content. + var titleBodyLabelsSizeToFit: Bool { get set } + + /** A boolean value specifying whether to enable/disable the addition of an extra padding to the message view so as + to prevent a visual gap from being displayed when the message is presented with the default spring animation. + + You most likely want to disable the extra padding when using the following properties: *cornerRadius*. + */ + var disableSpringAnimationPadding: Bool { get set } +} + +/// Default RMessage specs and stylings from which to construct messages. +public struct DefaultRMessageSpec: RMessageSpec { + public var backgroundColor = UIColor.white + public var targetAlpha = CGFloat(1) + public var cornerRadius = CGFloat(0) + public var verticalOffset = CGFloat(0) + public var iconImage: UIImage? + public var iconImageTintColor = UIColor.clear + public var iconImageRelativeCornerRadius = CGFloat(0) + public var backgroundImage: UIImage? + public var titleAttributes: [NSAttributedStringKey: Any]? + public var titleFont = UIFont.boldSystemFont(ofSize: 14) + public var titleColor = UIColor.black + public var titleShadowColor = UIColor.clear + public var titleShadowOffset = CGSize.zero + public var titleTextAlignment = NSTextAlignment.left + public var bodyAttributes: [NSAttributedStringKey: Any]? + public var bodyFont = UIFont.boldSystemFont(ofSize: 12) + public var bodyColor = UIColor.black + public var bodyShadowColor = UIColor.clear + public var bodyShadowOffset = CGSize.zero + public var bodyTextAlignment = NSTextAlignment.left + public var durationType = RMessageDuration.automatic + public var timeToDismiss = TimeInterval(-1.0) + public var blurBackground = false + public var titleBodyLabelsSizeToFit = false + public var disableSpringAnimationPadding = false +} + +// MARK: Implementation for the builtin message types + +public let errorSpec: RMessageSpec = { + var errorSpec = DefaultRMessageSpec() + errorSpec.backgroundColor = UIColor("#FF2D55") ?? UIColor.black + errorSpec.titleColor = UIColor.white + errorSpec.bodyColor = UIColor.white + errorSpec.iconImage = UIImage(named: "ErrorMessageIcon.png", in: Bundle(for: RMessage.self), compatibleWith: nil) + return errorSpec +}() + +public let warningSpec: RMessageSpec = { + var warningSpec = DefaultRMessageSpec() + warningSpec.backgroundColor = UIColor("#FFCC00") ?? UIColor.black + warningSpec.titleColor = UIColor("#484638") ?? UIColor.white + warningSpec.bodyColor = warningSpec.titleColor + warningSpec.iconImage = UIImage(named: "WarningMessageIcon.png", in: Bundle(for: RMessage.self), compatibleWith: nil) + return warningSpec +}() + +public let normalSpec: RMessageSpec = { + var normalSpec = DefaultRMessageSpec() + normalSpec.backgroundColor = UIColor("#E8E8E8") ?? UIColor.black + normalSpec.titleColor = UIColor("#727C83") ?? UIColor.white + normalSpec.bodyColor = normalSpec.titleColor + return normalSpec +}() + +public let successSpec: RMessageSpec = { + var successSpec = DefaultRMessageSpec() + successSpec.backgroundColor = UIColor("#00C060") ?? UIColor.black + successSpec.titleColor = UIColor.white + successSpec.bodyColor = successSpec.titleColor + successSpec.iconImage = UIImage(named: "SuccessMessageIcon.png", in: Bundle(for: RMessage.self), compatibleWith: nil) + return successSpec +}() diff --git a/RMessage/Resources/RMessage.xib b/Sources/Resources/RMessage.xib similarity index 100% rename from RMessage/Resources/RMessage.xib rename to Sources/Resources/RMessage.xib diff --git a/RMessageTests/Info.plist b/Tests/Info.plist similarity index 100% rename from RMessageTests/Info.plist rename to Tests/Info.plist diff --git a/Tests/RMessageTests.swift b/Tests/RMessageTests.swift new file mode 100644 index 0000000..e79631f --- /dev/null +++ b/Tests/RMessageTests.swift @@ -0,0 +1,30 @@ +// +// RMessageTests.swift +// RMessageTests +// +// Created by Adonis Peralta on 8/2/18. +// Copyright © 2018 None. All rights reserved. +// + +@testable import RMessage +import XCTest + +class RMessageTests: XCTestCase { + let rControl = RMController() + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + /// Test that RMessage does not attempt to present if there is no window hierarchy present. + func testQueuedMessages() { + for _ in 1 ... 6 { + rControl.showMessage(withSpec: DefaultRMessageSpec(), title: "Test") + } + XCTAssertTrue(rControl.queueCount == 0) + } +} diff --git a/RMessageUITests/Info.plist b/UITests/Info.plist similarity index 100% rename from RMessageUITests/Info.plist rename to UITests/Info.plist diff --git a/UITests/NavigationControllerTests.swift b/UITests/NavigationControllerTests.swift new file mode 100644 index 0000000..4788017 --- /dev/null +++ b/UITests/NavigationControllerTests.swift @@ -0,0 +1,190 @@ +// +// RMessageUITests.swift +// RMessageUITests +// +// Created by Adonis Peralta on 8/2/18. +// Copyright © 2018 None. All rights reserved. +// + +import Foundation +import XCTest + +class NavigationControllerTests: XCTestCase { + let app = XCUIApplication() + lazy var navBarElement = app.navigationBars.element + lazy var navBarFrame = navBarElement.frame + lazy var windowElement = app.windows.element.firstMatch + lazy var mainWindowFrame = windowElement.frame + lazy var notHittablePredicate = NSPredicate(format: "hittable == FALSE") + lazy var hittablePredicate = NSPredicate(format: "hittable == TRUE") + + // Check for message y position to hundredths place + let kMsgYPositionScale = 2 + + override func setUp() { + super.setUp() + continueAfterFailure = false + XCUIApplication().launch() + } + + func showMessageFromTopByPressingButton(withName buttonName: String, hidingNavBar: Bool, timeToShow displayTimeout: TimeInterval, timeToHide dismissTimeout: TimeInterval) { + if hidingNavBar { + app.buttons["Toggle NavBar"].tap() + } + + app.buttons[buttonName].tap() + + let displayedMessage = app.otherElements["RMessage"] + let padding = springAnimationPadding(forHeight: Float(displayedMessage.frame.size.height)) + let expectedMsgYPosition = hidingNavBar ? padding : Float(navBarFrame.size.height + navBarFrame.origin.y) + padding + + let messageDisplayed = displayedMessage.waitForExistence(timeout: displayTimeout) + + XCTAssert(messageDisplayed, "\(buttonName) message failed to display") + + let expectedMessagePositionValid = validateFloatsToScale( + Float(displayedMessage.frame.origin.y), + expectedMsgYPosition, scale: kMsgYPositionScale + ) + XCTAssert(expectedMessagePositionValid, "\(buttonName) message displayed in the wrong position") + + let exp = expectation(for: notHittablePredicate, evaluatedWith: displayedMessage, handler: nil) + wait(for: [exp], timeout: dismissTimeout) + } + + func showBottomMessage(hidingNavBar: Bool, withTimeout displayTimeout: TimeInterval, timeToHide dismissTimeout: TimeInterval) { + if hidingNavBar { + app.buttons["Toggle NavBar"].tap() + } + + let buttonName = "Bottom" + + app.buttons[buttonName].tap() + + let displayedMessage = app.otherElements["RMessage"] + let padding = springAnimationPadding(forHeight: Float(displayedMessage.frame.size.height)) + let expectedMsgYPosition = Float(mainWindowFrame.size.height - displayedMessage.frame.size.height) - padding + let messageDisplayed = displayedMessage.waitForExistence(timeout: displayTimeout) + + XCTAssert(messageDisplayed, "\(buttonName) message failed to display") + + let expectedMessagePositionValid = validateFloatsToScale( + Float(displayedMessage.frame.origin.y), + expectedMsgYPosition, scale: kMsgYPositionScale + ) + + XCTAssert(expectedMessagePositionValid, "\(buttonName) message displayed in the wrong position") + let exp = expectation(for: notHittablePredicate, evaluatedWith: displayedMessage, handler: nil) + wait(for: [exp], timeout: dismissTimeout) + } + + func showEndlessMessage(hidingNavBar: Bool, withTimeout displayTimeout: TimeInterval) { + if hidingNavBar { + app.buttons["Toggle NavBar"].tap() + } + + let buttonName = "Endless" + + app.buttons[buttonName].tap() + + let displayedMessage = app.otherElements["RMessage"] + + // The spring animation padding is calculated on the message size prior to accounting for the safe area layout + // guides so to properly determine the position of the view we must remove any safe layout are guide sizing from + // height of the view prior to attempting to calculate the spring animation padding. + let padding = springAnimationPadding(forHeight: Float(displayedMessage.frame.size.height - 20.0)) + let expectedMsgYPosition = hidingNavBar ? padding : Float(navBarFrame.size.height + navBarFrame.origin.y) + padding + let messageDisplayed = displayedMessage.waitForExistence(timeout: displayTimeout) + + XCTAssert(messageDisplayed, "\(buttonName) message failed to display") + + let expectedMessagePositionValid = validateFloatsToScale( + Float(displayedMessage.frame.origin.y), + expectedMsgYPosition, scale: kMsgYPositionScale + ) + XCTAssert(expectedMessagePositionValid, "\(buttonName) message displayed in the wrong position") + + sleep(20) + XCTAssert(displayedMessage.isHittable, "\(buttonName) message no longer on screen") + } + + func testErrorMessage() { + showMessageFromTopByPressingButton(withName: "Error", hidingNavBar: false, timeToShow: 3.0, timeToHide: 8.0) + } + + func testNormalMessage() { + showMessageFromTopByPressingButton(withName: "Message", hidingNavBar: false, timeToShow: 3.0, timeToHide: 8.0) + } + + func testWarningMessage() { + showMessageFromTopByPressingButton(withName: "Warning", hidingNavBar: false, timeToShow: 3.0, timeToHide: 8.0) + } + + func testSuccessMessage() { + showMessageFromTopByPressingButton(withName: "Success", hidingNavBar: false, timeToShow: 3.0, timeToHide: 8.0) + } + + func testLongMessage() { + showMessageFromTopByPressingButton(withName: "Text", hidingNavBar: false, timeToShow: 3.0, timeToHide: 12.0) + } + + func testButtonMessage() { + showMessageFromTopByPressingButton(withName: "Button", hidingNavBar: false, timeToShow: 3.0, timeToHide: 8.0) + } + + func testBottomMessage() { + showBottomMessage(hidingNavBar: false, withTimeout: 3.0, timeToHide: 8.0) + } + + func testCustomMessage() { + showMessageFromTopByPressingButton(withName: "Custom design", hidingNavBar: false, timeToShow: 3.0, timeToHide: 8.0) + } + + func testImageMessage() { + showMessageFromTopByPressingButton(withName: "Custom image", hidingNavBar: false, timeToShow: 3.0, timeToHide: 8.0) + } + + func testEndlessMessage() { + showEndlessMessage(hidingNavBar: false, withTimeout: 3.0) + } + + func testErrorMessageNoNavBar() { + showMessageFromTopByPressingButton(withName: "Error", hidingNavBar: true, timeToShow: 3.0, timeToHide: 8.0) + } + + func testNormalMessageNoNavBar() { + showMessageFromTopByPressingButton(withName: "Message", hidingNavBar: true, timeToShow: 3.0, timeToHide: 8.0) + } + + func testWarningMessageNoNavBar() { + showMessageFromTopByPressingButton(withName: "Warning", hidingNavBar: true, timeToShow: 3.0, timeToHide: 8.0) + } + + func testSuccessMessageNoNavBar() { + showMessageFromTopByPressingButton(withName: "Success", hidingNavBar: true, timeToShow: 3.0, timeToHide: 8.0) + } + + func testLongMessageNoNavBar() { + showMessageFromTopByPressingButton(withName: "Text", hidingNavBar: true, timeToShow: 3.0, timeToHide: 12.0) + } + + func testButtonMessageNoNavBar() { + showMessageFromTopByPressingButton(withName: "Button", hidingNavBar: true, timeToShow: 3.0, timeToHide: 8.0) + } + + func testBottomMessageNoNavBar() { + showBottomMessage(hidingNavBar: false, withTimeout: 3.0, timeToHide: 8.0) + } + + func testCustomMessageNoNavBar() { + showMessageFromTopByPressingButton(withName: "Custom design", hidingNavBar: false, timeToShow: 3.0, timeToHide: 8.0) + } + + func testImageMessageNoNavBar() { + showMessageFromTopByPressingButton(withName: "Custom image", hidingNavBar: false, timeToShow: 3.0, timeToHide: 8.0) + } + + func testEndlessMessageNoNavBar() { + showEndlessMessage(hidingNavBar: false, withTimeout: 3.0) + } +} diff --git a/UITests/TestHelpers.swift b/UITests/TestHelpers.swift new file mode 100644 index 0000000..c3e540a --- /dev/null +++ b/UITests/TestHelpers.swift @@ -0,0 +1,22 @@ +// +// TestHelpers.swift +// RMessage +// +// Created by Adonis Peralta on 9/9/18. +// Copyright © 2018 None. All rights reserved. +// + +import Foundation + +// Returns true or false if the floats are equal to each other after truncating all numbers +// after the scale decimal places +func validateFloatsToScale(_ f1: Float, _ f2: Float, scale: Int) -> Bool { + let truncFactor = powf(10.0, Float(scale)) + let tFloat1 = truncf(f1 * truncFactor) / truncFactor + let tFloat2 = truncf(f2 * truncFactor) / truncFactor + return tFloat1 == tFloat2 +} + +func springAnimationPadding(forHeight height: Float) -> Float { + return ceilf(height / 120) * -5.0 +} diff --git a/UITests/ViewControllerTests.swift b/UITests/ViewControllerTests.swift new file mode 100644 index 0000000..33fe671 --- /dev/null +++ b/UITests/ViewControllerTests.swift @@ -0,0 +1,222 @@ +// +// ViewControllerTests.swift +// RMessageUITests +// +// Created by Adonis Peralta on 9/9/18. +// Copyright © 2018 None. All rights reserved. +// + +import XCTest + +class ViewControllerTests: XCTestCase { + let app = XCUIApplication() + lazy var navBarElement = app.navigationBars.element + lazy var windowElement = app.windows.element.firstMatch + lazy var mainWindowFrame = windowElement.frame + lazy var notHittablePredicate = NSPredicate(format: "hittable == FALSE") + lazy var hittablePredicate = NSPredicate(format: "hittable == TRUE") + + // Check for message y position to hundredths place + let kMsgYPositionScale = 2 + + override func setUp() { + super.setUp() + continueAfterFailure = false + XCUIApplication().launch() + } + + func showMessageFromTopByPressingButton(withName buttonName: String, timeToShow displayTimeout: TimeInterval, timeToHide dismissTimeout: TimeInterval) { + app.buttons["Present Modal"].tap() + app.buttons[buttonName].tap() + + let displayedMessage = app.otherElements["RMessage"] + let padding = springAnimationPadding(forHeight: Float(displayedMessage.frame.size.height)) + let expectedMsgYPosition = padding + let messageDisplayed = displayedMessage.waitForExistence(timeout: displayTimeout) + + XCTAssert(messageDisplayed, "\(buttonName) message failed to display") + + let expectedMessagePositionValid = validateFloatsToScale( + Float(displayedMessage.frame.origin.y), + expectedMsgYPosition, scale: kMsgYPositionScale + ) + XCTAssert(expectedMessagePositionValid, "\(buttonName) message displayed in the wrong position") + + let exp = expectation(for: notHittablePredicate, evaluatedWith: displayedMessage, handler: nil) + wait(for: [exp], timeout: dismissTimeout) + } + + func showBottomMessage(withTimeout displayTimeout: TimeInterval, timeToHide dismissTimeout: TimeInterval) { + let buttonName = "Bottom" + app.buttons["Present Modal"].tap() + app.buttons[buttonName].tap() + + let displayedMessage = app.otherElements["RMessage"] + let padding = springAnimationPadding(forHeight: Float(displayedMessage.frame.size.height)) + let expectedMsgYPosition = Float(mainWindowFrame.size.height - displayedMessage.frame.size.height) - padding + let messageDisplayed = displayedMessage.waitForExistence(timeout: displayTimeout) + + XCTAssert(messageDisplayed, "\(buttonName) message failed to display") + + let expectedMessagePositionValid = validateFloatsToScale( + Float(displayedMessage.frame.origin.y), + expectedMsgYPosition, scale: kMsgYPositionScale + ) + + XCTAssert(expectedMessagePositionValid, "\(buttonName) message displayed in the wrong position") + let exp = expectation(for: notHittablePredicate, evaluatedWith: displayedMessage, handler: nil) + wait(for: [exp], timeout: dismissTimeout) + } + + func showEndlessMessage(withTimeout displayTimeout: TimeInterval) { + let buttonName = "Endless" + app.buttons["Present Modal"].tap() + app.buttons[buttonName].tap() + + let displayedMessage = app.otherElements["RMessage"] + + // The spring animation padding is calculated on the message size prior to accounting for the safe area layout + // guides so to properly determine the position of the view we must remove any safe layout are guide sizing from + // height of the view prior to attempting to calculate the spring animation padding. + let padding = springAnimationPadding(forHeight: Float(displayedMessage.frame.size.height - 20.0)) + let expectedMsgYPosition = padding + let messageDisplayed = displayedMessage.waitForExistence(timeout: displayTimeout) + + XCTAssert(messageDisplayed, "\(buttonName) message failed to display") + + let expectedMessagePositionValid = validateFloatsToScale( + Float(displayedMessage.frame.origin.y), + expectedMsgYPosition, scale: kMsgYPositionScale + ) + XCTAssert(expectedMessagePositionValid, "\(buttonName) message displayed in the wrong position") + + sleep(20) + XCTAssert(displayedMessage.isHittable, "\(buttonName) message no longer on screen") + } + + func testButtonMessageButtonPress() { + let buttonName = "Button" + app.buttons["Present Modal"].tap() + app.buttons[buttonName].tap() + + let displayedMessage = app.otherElements["RMessage"] + var messageDisplayed = displayedMessage.waitForExistence(timeout: 3.0) + + XCTAssert(messageDisplayed, "\(buttonName) message failed to display") + + let displayedMessageDismissedExpectation = expectation(for: notHittablePredicate, evaluatedWith: displayedMessage, handler: nil) + app.buttons["Update"].tap() + + let updateMessage = app.otherElements["RMessage"] + messageDisplayed = updateMessage.waitForExistence(timeout: 3.0) + + XCTAssert(messageDisplayed, "Update message failed to display") + + let updateMessageDismissedExpectation = expectation(for: notHittablePredicate, evaluatedWith: updateMessage, handler: nil) + wait(for: [displayedMessageDismissedExpectation], timeout: 5.0) + wait(for: [updateMessageDismissedExpectation], timeout: 5.0) + } + + func testEndlessMessageDismiss() { + let buttonName = "Endless" + app.buttons["Present Modal"].tap() + app.buttons[buttonName].tap() + + let displayedMessage = app.otherElements["RMessage"] + let padding = springAnimationPadding(forHeight: Float(displayedMessage.frame.size.height - 20.0)) + let expectedMsgYPosition = padding + + let messageDisplayed = displayedMessage.waitForExistence(timeout: 3.0) + + XCTAssert(messageDisplayed, "\(buttonName) message failed to display") + + let expectedMessagePositionValid = validateFloatsToScale( + Float(displayedMessage.frame.origin.y), + expectedMsgYPosition, scale: kMsgYPositionScale + ) + XCTAssert(expectedMessagePositionValid, "\(buttonName) message displayed in the wrong position") + + sleep(20) + + XCTAssert(displayedMessage.isHittable, "\(buttonName) message no longer on screen") + + displayedMessage.tap() + sleep(5) + XCTAssert(displayedMessage.isHittable, "\(buttonName) message no longer on screen") + + app.buttons["Dismiss"].tap() + + let messageDidDismiss = expectation(for: notHittablePredicate, evaluatedWith: displayedMessage, handler: nil) + wait(for: [messageDidDismiss], timeout: 5.0) + } + + func testTapToDismiss() { + let buttonName = "Error" + app.buttons["Present Modal"].tap() + app.buttons[buttonName].tap() + + let displayedMessage = app.otherElements["RMessage"] + let padding = springAnimationPadding(forHeight: Float(displayedMessage.frame.size.height)) + let expectedMsgYPosition = padding + + let messageDisplayed = displayedMessage.waitForExistence(timeout: 3.0) + + XCTAssert(messageDisplayed, "\(buttonName) message failed to display") + + let expectedMessagePositionValid = validateFloatsToScale( + Float(displayedMessage.frame.origin.y), + expectedMsgYPosition, scale: kMsgYPositionScale + ) + + XCTAssert(expectedMessagePositionValid, "\(buttonName) message displayed in the wrong position") + + displayedMessage.tap() + let exp = expectation(for: notHittablePredicate, evaluatedWith: displayedMessage, handler: nil) + wait(for: [exp], timeout: 2.0) + } + + func testErrorMessage() { + showMessageFromTopByPressingButton(withName: "Error", timeToShow: 3.0, timeToHide: 8.0) + } + + func testNormalMessage() { + showMessageFromTopByPressingButton(withName: "Message", timeToShow: 3.0, timeToHide: 8.0) + } + + func testWarningMessage() { + showMessageFromTopByPressingButton(withName: "Warning", timeToShow: 3.0, timeToHide: 8.0) + } + + func testSuccessMessage() { + showMessageFromTopByPressingButton(withName: "Success", timeToShow: 3.0, timeToHide: 8.0) + } + + func testLongMessage() { + showMessageFromTopByPressingButton(withName: "Text", timeToShow: 3.0, timeToHide: 12.0) + } + + func testButtonMessage() { + showMessageFromTopByPressingButton(withName: "Button", timeToShow: 3.0, timeToHide: 8.0) + } + + func testBottomMessage() { + showBottomMessage(withTimeout: 3.0, timeToHide: 8.0) + } + + func testCustomMessage() { + showMessageFromTopByPressingButton(withName: "Custom design", timeToShow: 3.0, timeToHide: 8.0) + } + + func testImageMessage() { + showMessageFromTopByPressingButton(withName: "Custom image", timeToShow: 3.0, timeToHide: 8.0) + } + + func testEndlessMessage() { + showEndlessMessage(withTimeout: 3.0) + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } +}