Compare commits

...

51 Commits

Author SHA1 Message Date
igork-ramotion 4534c0dcd4 Merge pull request #64 from nzufelt/patch-1
Fix typo!
2020-04-09 13:59:56 +03:00
Nicholas Zufelt b24752e27a Fix typo! 2020-04-08 16:30:13 -04:00
Ramotion 22ef289da4 Update README.md 2020-04-06 09:54:27 +03:00
igor.k 1428534fcc fix problems with running on simulator (issue #61) 2019-11-13 00:45:34 +03:00
igor.k 3ee0a8a606 add SPM config and Ramotion application icon set 2019-11-13 00:34:54 +03:00
Ramotion 0f35c65a48 Update README.md 2019-10-12 21:11:13 +03:00
Alex K c578516270 bump version 2019-04-03 15:41:12 +03:00
Alex K 1d00b258e9 converted to swift 5 2019-04-03 15:38:21 +03:00
Ramotion 37940a69eb Update README.md 2018-12-28 12:52:30 +03:00
Ramotion 739c698c02 Add files via upload 2018-12-28 01:44:28 -08:00
Ramotion 3289f9c315 Update README.md 2018-12-10 11:31:41 +03:00
Ramotion 82f01134d2 Update README.md 2018-12-10 10:21:29 +03:00
Alex K f6d7e82c98 update podspec 2018-10-13 11:24:54 +03:00
Alex 5c8a416f5b Merge pull request #57 from tache/master
added circle open delegate
2018-10-13 11:20:36 +03:00
tache 0122f09e6b Merged with upstream 2018-10-13 00:34:14 -04:00
Alex K 98a4fa1db9 swift 4.2 2018-09-27 09:09:58 +03:00
tache a766ceddbd Updated to Swift 4.2 2018-09-24 14:15:00 -04:00
Alex K 6d961f5923 update podspec 2018-07-02 10:04:48 +03:00
Alex cf2619bbd8 Merge pull request #55 from theyongdai/master
`customNormalIconView` and `customSelectedIconView` can't be nil. I'm…
2018-07-02 09:19:54 +03:00
Michael Tai d378620f8b customNormalIconView and customSelectedIconView can't be nil. I'm using ionicons-fonts not images. I can't see the reason why I need to set any images. (7f2a3f8) It might be legacy code form wayback when the delegate methods were introduced or software engineering bureaucracy. Either way, good job Alex.k for making this project possible. Only he could just push a tiny bit further :p
See issue50 https://github.com/Ramotion/circle-menu/issues/50
2018-06-29 12:54:31 -04:00
Ramotion e02c17db4f Update README.md 2018-06-29 14:26:58 +03:00
Ramotion cb88f53fc6 Update README.md 2018-06-26 11:12:29 +03:00
Ramotion 5826edf31f Update README.md 2018-05-29 12:06:08 +03:00
Ramotion ec9a6fc6a0 Update README.md 2018-04-28 12:32:46 +03:00
Ramotion 6d45896997 Add files via upload 2018-04-28 02:31:44 -07:00
Ramotion 0c2b2c49ee Delete preview.gif 2018-04-28 12:30:52 +03:00
Ramotion bdb3ccb994 Update README.md 2018-04-20 11:10:35 +03:00
Alex K 51627a3f3d update podspec 2018-04-18 15:04:01 +03:00
Alex K 68881fb3cc remove warnings and make customImages public 2018-04-18 11:36:38 +03:00
Ramotion 83d32f754b Update README.md 2018-03-06 12:28:21 +03:00
Alex Mikhnev 628a4bab8a Update README.md 2018-02-21 14:47:43 +03:00
Alex K 2c9a57acdc update podspec 2018-01-23 11:22:47 +03:00
Alex db7ca734e9 Merge pull request #48 from derekvallar/master
StartAngle/EndAngle feature added.
2018-01-23 11:15:00 +03:00
derekvallar f4f645548d StartAngle/EndAngle feature added. 2018-01-22 22:50:49 -08:00
Alex K c520680100 Fixes #20 2018-01-22 16:34:47 +03:00
Alex K 04e27502df update podspec 2018-01-22 12:59:35 +03:00
tache 3968b56d02 Update to Swift 4 2017-11-03 20:34:04 -04:00
tache fc589564b4 no message 2017-11-03 20:19:42 -04:00
tache 41df3a1714 Merge remote-tracking branch 'upstream/master' 2017-09-28 10:22:59 -04:00
tache 5782a0fafa Merge branch 'master' of github.com:tache/circle-menu
* 'master' of github.com:tache/circle-menu:
  Updated to Swift 3.2
  Updated for 8.3 and optional callbacks
  updating readme document
  Added in an open menu and made open/close delegates fire asynchronously
  Moved the collapse to the proper location - should only fire once
2017-04-03 19:19:11 -04:00
tache 0652edf64d Updated to Swift 3.2 2017-04-03 19:18:48 -04:00
tache 1fffcfb25e Updated for 8.3 and optional callbacks 2017-04-03 19:18:48 -04:00
tache 3f3a676a6d updating readme document 2017-04-03 19:18:48 -04:00
tache 7a6175e8fb Added in an open menu and made open/close delegates fire asynchronously 2017-04-03 19:18:48 -04:00
tache 6cf24b1ee1 Moved the collapse to the proper location - should only fire once 2017-04-03 19:18:48 -04:00
tache c37e32ce3f Updated to Swift 3.2 2017-04-03 19:13:50 -04:00
tache 38926b111a Updated for 8.3 and optional callbacks 2017-04-03 19:09:48 -04:00
tache dce71006c9 updating readme document 2017-01-15 10:53:52 -05:00
tache 77ae3b58c9 Added in an open menu and made open/close delegates fire asynchronously 2017-01-15 10:30:19 -05:00
Chris Graham 2d23e4f7f3 Merge pull request #1 from tache/fix-collapse-delegate
Moved the collapse to the proper location - should only fire once
2017-01-15 08:46:16 -05:00
tache 7e95b925a5 Moved the collapse to the proper location - should only fire once 2017-01-15 08:39:22 -05:00
30 changed files with 372 additions and 197 deletions
+1 -1
View File
@@ -1 +1 @@
4.0
4.2
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'CircleMenu'
s.version = '3.0.2'
s.version = '4.1.0'
s.summary = 'Amazing animation with buttons'
s.homepage = 'https://github.com/Ramotion/circle-menu'
s.license = 'MIT'
+53 -45
View File
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
@@ -17,7 +17,6 @@
841EC5911C58E898008872D5 /* CircleMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841EC58F1C58E898008872D5 /* CircleMenuButton.swift */; };
841EC5941C58F3E2008872D5 /* CircleMenuLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841EC5931C58F3E2008872D5 /* CircleMenuLoader.swift */; };
8497460C1C6A1C5D001E7184 /* CircleMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841EC58D1C58E898008872D5 /* CircleMenu.swift */; };
8497460D1C6A1C6D001E7184 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84F248BD1C58E65F008F12C1 /* Assets.xcassets */; };
8497460E1C6A1C9D001E7184 /* CircleMenuLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841EC5931C58F3E2008872D5 /* CircleMenuLoader.swift */; };
8497460F1C6A1CA0001E7184 /* CircleMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841EC58F1C58E898008872D5 /* CircleMenuButton.swift */; };
84F248B71C58E65F008F12C1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F248B61C58E65F008F12C1 /* AppDelegate.swift */; };
@@ -72,7 +71,6 @@
84F248BB1C58E65F008F12C1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
84F248BD1C58E65F008F12C1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
84F248C01C58E65F008F12C1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
84F248C21C58E65F008F12C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
84F248C71C58E65F008F12C1 /* CircleMenuTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CircleMenuTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
84F248CB1C58E65F008F12C1 /* CircleMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleMenuTests.swift; sourceTree = "<group>"; };
84F248CD1C58E65F008F12C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -168,7 +166,6 @@
84F248BA1C58E65F008F12C1 /* Main.storyboard */,
84F248BD1C58E65F008F12C1 /* Assets.xcassets */,
84F248BF1C58E65F008F12C1 /* LaunchScreen.storyboard */,
84F248C21C58E65F008F12C1 /* Info.plist */,
);
path = CircleMenu;
sourceTree = "<group>";
@@ -221,7 +218,6 @@
84F248AF1C58E65F008F12C1 /* Sources */,
84F248B01C58E65F008F12C1 /* Frameworks */,
84F248B11C58E65F008F12C1 /* Resources */,
846980FB1C59F398002D77BE /* swift lint */,
8403F5851CFF2C2E007D0BD1 /* Embed Frameworks */,
);
buildRules = (
@@ -259,27 +255,28 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0920;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "Alex K.";
TargetAttributes = {
8403F5781CFF2C2E007D0BD1 = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 0900;
LastSwiftMigration = 1020;
};
84F248B21C58E65F008F12C1 = {
CreatedOnToolsVersion = 7.2;
DevelopmentTeam = 34MUF9YXTA;
LastSwiftMigration = 0900;
LastSwiftMigration = 1020;
};
84F248C61C58E65F008F12C1 = {
CreatedOnToolsVersion = 7.2;
LastSwiftMigration = 1020;
TestTargetID = 84F248B21C58E65F008F12C1;
};
};
};
buildConfigurationList = 84F248AE1C58E65F008F12C1 /* Build configuration list for PBXProject "CircleMenu" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
compatibilityVersion = "Xcode 10.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@@ -319,29 +316,11 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8497460D1C6A1C6D001E7184 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
846980FB1C59F398002D77BE /* swift lint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "swift lint";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
8403F5741CFF2C2E007D0BD1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -424,12 +403,15 @@
INFOPLIST_FILE = CircleMenu/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.CircleMenu;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@@ -449,12 +431,15 @@
INFOPLIST_FILE = CircleMenu/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.CircleMenu;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@@ -465,6 +450,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -473,12 +459,14 @@
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -510,7 +498,7 @@
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
};
name = Debug;
};
@@ -518,6 +506,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -526,12 +515,14 @@
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -555,8 +546,9 @@
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 4.2;
VALIDATE_PRODUCT = YES;
};
name = Release;
@@ -567,14 +559,17 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 1;
INFOPLIST_FILE = CircleMenu/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.dev;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
@@ -584,14 +579,17 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 1;
INFOPLIST_FILE = CircleMenu/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.dev;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
};
name = Release;
};
@@ -600,9 +598,14 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
INFOPLIST_FILE = CircleMenuTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.CircleMenuTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CircleMenuDemo.app/CircleMenuDemo";
};
name = Debug;
@@ -612,9 +615,14 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
INFOPLIST_FILE = CircleMenuTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.CircleMenuTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CircleMenuDemo.app/CircleMenuDemo";
};
name = Release;
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0920"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,7 +26,6 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
@@ -37,7 +36,6 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
+1 -1
View File
@@ -15,7 +15,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
}
+75 -9
View File
@@ -1,53 +1,119 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon-40.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon-60.png",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon-58.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon-87.png",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon-80.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon-120.png",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon-120.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon-180.png",
"scale" : "3x"
},
{
"idiom" : "ios-marketing",
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon-20.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon-40.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon-29.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon-58.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon-40.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon-80.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon-76.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon-152.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "icon-167.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Ramotion1024.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"pre-rendered" : true
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

+1 -1
View File
@@ -26,7 +26,7 @@ class ViewController: UIViewController, CircleMenuDelegate {
("icon_search", UIColor(red: 0.22, green: 0.74, blue: 0, alpha: 1)),
("notifications-btn", UIColor(red: 0.96, green: 0.23, blue: 0.21, alpha: 1)),
("settings-btn", UIColor(red: 0.51, green: 0.15, blue: 1, alpha: 1)),
("nearby-btn", UIColor(red: 1, green: 0.39, blue: 0, alpha: 1)),
("nearby-btn", UIColor(red: 1, green: 0.39, blue: 0, alpha: 1))
]
override func viewDidLoad() {
super.viewDidLoad()
+120 -80
View File
@@ -26,7 +26,7 @@ import UIKit
// MARK: helpers
func Init<Type>(_ value: Type, block: (_ object: Type) -> Void) -> Type {
func customize<Type>(_ value: Type, block: (_ object: Type) -> Void) -> Type {
block(value)
return value
}
@@ -66,11 +66,18 @@ func Init<Type>(_ value: Type, block: (_ object: Type) -> Void) -> Type {
@objc optional func circleMenu(_ circleMenu: CircleMenu, buttonDidSelected button: UIButton, atIndex: Int)
/**
Tells the delegate that the menu was collapsed - the cancel action.
Tells the delegate that the menu was collapsed - the cancel action. Fires immediately on button press
- parameter circleMenu: A circle menu object informing the delegate about the new index selection.
*/
@objc optional func menuCollapsed(_ circleMenu: CircleMenu)
/**
Tells the delegate that the menu was opened. Fires immediately on button press
- parameter circleMenu: A circle menu object informing the delegate about the new index selection.
*/
@objc optional func menuOpened(_ circleMenu: CircleMenu)
}
// MARK: CircleMenu
@@ -88,18 +95,29 @@ open class CircleMenu: UIButton {
@IBInspectable open var distance: Float = 100
/// Delay between show buttons
@IBInspectable open var showDelay: Double = 0
/// Start angle of the circle
@IBInspectable open var startAngle: Float = 0
/// End angle of the circle
@IBInspectable open var endAngle: Float = 360
// Pop buttons radius, if nil use center button size
open var subButtonsRadius: CGFloat?
// Show buttons event
open var showButtonsEvent: UIControl.Event = UIControl.Event.touchUpInside {
didSet {
addActions(newEvent: showButtonsEvent, oldEvent: oldValue)
}
}
/// The object that acts as the delegate of the circle menu.
@IBOutlet open var delegate: AnyObject? // CircleMenuDelegate?
@IBOutlet open weak var delegate: AnyObject? // CircleMenuDelegate?
var buttons: [UIButton]?
weak var platform: UIView?
fileprivate var customNormalIconView: UIImageView?
fileprivate var customSelectedIconView: UIImageView?
public var customNormalIconView: UIImageView?
public var customSelectedIconView: UIImageView?
/**
Initializes and returns a circle menu object.
@@ -122,7 +140,7 @@ open class CircleMenu: UIButton {
super.init(frame: frame)
if let icon = normalIcon {
setImage(UIImage(named: icon), for: UIControlState())
setImage(UIImage(named: icon), for: .normal)
}
if let icon = selectedIcon {
@@ -143,15 +161,14 @@ open class CircleMenu: UIButton {
}
fileprivate func commonInit() {
addActions()
addActions(newEvent: showButtonsEvent)
customNormalIconView = addCustomImageView(state: UIControlState())
customNormalIconView = addCustomImageView(state: .normal)
customSelectedIconView = addCustomImageView(state: .selected)
if customSelectedIconView != nil {
customSelectedIconView?.alpha = 0
}
setImage(UIImage(), for: UIControlState())
customSelectedIconView?.alpha = 0
setImage(UIImage(), for: .normal)
setImage(UIImage(), for: .selected)
}
@@ -170,7 +187,7 @@ open class CircleMenu: UIButton {
buttonsAnimationIsShow(isShow: false, duration: duration, hideDelay: hideDelay)
tapBounceAnimation()
tapBounceAnimation(duration: 0.5)
tapRotatedAnimation(0.3, isSelected: false)
}
@@ -188,22 +205,23 @@ open class CircleMenu: UIButton {
}
}
return true
}
open override func removeFromSuperview() {
if self.platform?.superview != nil { self.platform?.removeFromSuperview() }
super.removeFromSuperview()
}
open override func removeFromSuperview() {
if self.platform?.superview != nil { self.platform?.removeFromSuperview() }
super.removeFromSuperview()
}
// MARK: create
fileprivate func createButtons(platform: UIView) -> [UIButton] {
var buttons = [UIButton]()
let step: Float = 360.0 / Float(buttonsCount)
let step = getArcStep()
for index in 0 ..< buttonsCount {
let angle: Float = Float(index) * step
let angle: Float = startAngle + Float(index) * step
let distance = Float(bounds.size.height / 2.0)
let buttonSize: CGSize
if let subButtonsRadius = self.subButtonsRadius {
@@ -211,9 +229,9 @@ open class CircleMenu: UIButton {
} else {
buttonSize = bounds.size
}
let button = Init(CircleMenuButton(size: buttonSize, platform: platform, distance: distance, angle: angle)) {
let button = customize(CircleMenuButton(size: buttonSize, platform: platform, distance: distance, angle: angle)) {
$0.tag = index
$0.addTarget(self, action: #selector(CircleMenu.buttonHandler(_:)), for: UIControlEvents.touchUpInside)
$0.addTarget(self, action: #selector(CircleMenu.buttonHandler(_:)), for: UIControl.Event.touchUpInside)
$0.alpha = 0
}
buttons.append(button)
@@ -221,12 +239,12 @@ open class CircleMenu: UIButton {
return buttons
}
fileprivate func addCustomImageView(state: UIControlState) -> UIImageView? {
fileprivate func addCustomImageView(state: UIControl.State) -> UIImageView? {
guard let image = image(for: state) else {
return nil
}
let iconView = Init(UIImageView(image: image)) {
let iconView = customize(UIImageView(image: image)) {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.contentMode = .center
$0.isUserInteractionEnabled = false
@@ -250,14 +268,14 @@ open class CircleMenu: UIButton {
}
fileprivate func createPlatform() -> UIView {
let platform = Init(UIView(frame: .zero)) {
let platform = customize(UIView(frame: .zero)) {
$0.backgroundColor = .clear
$0.translatesAutoresizingMaskIntoConstraints = false
}
superview?.insertSubview(platform, belowSubview: self)
// constraints
let sizeConstraints = [NSLayoutAttribute.width, .height].map {
let sizeConstraints = [NSLayoutConstraint.Attribute.width, .height].map {
NSLayoutConstraint(item: platform,
attribute: $0,
relatedBy: .equal,
@@ -268,7 +286,7 @@ open class CircleMenu: UIButton {
}
platform.addConstraints(sizeConstraints)
let centerConstraints = [NSLayoutAttribute.centerX, .centerY].map {
let centerConstraints = [NSLayoutConstraint.Attribute.centerX, .centerY].map {
NSLayoutConstraint(item: self,
attribute: $0,
relatedBy: .equal,
@@ -284,23 +302,50 @@ open class CircleMenu: UIButton {
// MARK: configure
fileprivate func addActions() {
addTarget(self, action: #selector(CircleMenu.onTap), for: UIControlEvents.touchUpInside)
fileprivate func addActions(newEvent: UIControl.Event, oldEvent: UIControl.Event? = nil) {
if let oldEvent = oldEvent { removeTarget(self, action: #selector(CircleMenu.onTap), for: oldEvent) }
addTarget(self, action: #selector(CircleMenu.onTap), for: newEvent)
}
/**
Retrieves the incremental lengths between buttons. If the arc length is 360 degrees or more, the increments
will evenly space out in a full circle. If the arc length is less than 360 degrees, the last button will be
placed on the endAngle.
*/
fileprivate func getArcStep() -> Float {
var arcLength = endAngle - startAngle
var stepCount = buttonsCount
if arcLength < 360 {
stepCount -= 1
} else if arcLength > 360 {
arcLength = 360
}
return arcLength / Float(stepCount)
}
// MARK: actions
private var isBounceAnimating: Bool = false
@objc func onTap() {
guard isBounceAnimating == false else { return }
isBounceAnimating = true
if buttonsIsShown() == false {
let platform = createPlatform()
buttons = createButtons(platform: platform)
self.platform = platform
DispatchQueue.main.asyncAfter(deadline: .now()) {
self.delegate?.menuOpened?(self)
}
}
let isShow = !buttonsIsShown()
let duration = isShow ? 0.5 : 0.2
buttonsAnimationIsShow(isShow: isShow, duration: duration)
tapBounceAnimation()
tapBounceAnimation(duration: 0.5) { [weak self] _ in self?.isBounceAnimating = false }
tapRotatedAnimation(0.3, isSelected: isShow)
}
@@ -311,7 +356,7 @@ open class CircleMenu: UIButton {
let strokeWidth: CGFloat
if let radius = self.subButtonsRadius {
strokeWidth = radius * 2
strokeWidth = radius * 2
} else {
strokeWidth = bounds.size.height
}
@@ -323,28 +368,24 @@ open class CircleMenu: UIButton {
if let container = sender.container { // rotation animation
sender.rotationAnimation(container.angleZ + 360, duration: duration)
container.superview?.bringSubview(toFront: container)
container.superview?.bringSubviewToFront(container)
}
if let buttons = buttons {
circle.fillAnimation(duration, startAngle: -90 + Float(360 / buttons.count) * Float(sender.tag)) { [weak self] in
self?.buttons?.forEach { $0.alpha = 0 }
}
circle.hideAnimation(0.5, delay: duration) { [weak self] in
if self?.platform?.superview != nil { self?.platform?.removeFromSuperview() }
}
hideCenterButton(duration: 0.3)
showCenterButton(duration: 0.525, delay: duration)
if customNormalIconView != nil && customSelectedIconView != nil {
DispatchQueue.main.asyncAfter(deadline: .now() + duration, execute: {
self.delegate?.circleMenu?(self, buttonDidSelected: sender, atIndex: sender.tag)
})
}
let step = getArcStep()
circle.fillAnimation(duration, startAngle: -90 + startAngle + step * Float(sender.tag)) { [weak self] in
self?.buttons?.forEach { $0.alpha = 0 }
}
}
circle.hideAnimation(0.5, delay: duration) { [weak self] in
if self?.platform?.superview != nil { self?.platform?.removeFromSuperview() }
}
hideCenterButton(duration: 0.3)
showCenterButton(duration: 0.525, delay: duration)
DispatchQueue.main.asyncAfter(deadline: .now() + duration, execute: {
self.delegate?.circleMenu?(self, buttonDidSelected: sender, atIndex: sender.tag)
})
}
// MARK: animations
@@ -353,13 +394,12 @@ open class CircleMenu: UIButton {
return
}
let step: Float = 360.0 / Float(buttonsCount)
let step = getArcStep()
for index in 0 ..< buttonsCount {
guard case let button as CircleMenuButton = buttons[index] else { continue }
let angle: Float = Float(index) * step
if isShow == true {
delegate?.circleMenu?(self, willDisplay: button, atIndex: index)
let angle: Float = startAngle + Float(index) * step
button.rotatedZ(angle: angle, animated: false, delay: Double(index) * showDelay)
button.showAnimation(distance: distance, duration: duration, delay: Double(index) * showDelay)
} else {
@@ -375,14 +415,14 @@ open class CircleMenu: UIButton {
}
}
fileprivate func tapBounceAnimation() {
fileprivate func tapBounceAnimation(duration: TimeInterval, completion: ((Bool)->())? = nil) {
transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 5,
options: UIViewAnimationOptions.curveLinear,
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 5,
options: UIView.AnimationOptions.curveLinear,
animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: 1, y: 1)
},
completion: nil)
self.transform = CGAffineTransform(scaleX: 1, y: 1)
},
completion: completion)
}
fileprivate func tapRotatedAnimation(_ duration: Float, isSelected: Bool) {
@@ -403,25 +443,25 @@ open class CircleMenu: UIButton {
toOpacity = 1
}
let rotation = Init(CABasicAnimation(keyPath: "transform.rotation")) {
let rotation = customize(CABasicAnimation(keyPath: "transform.rotation")) {
$0.duration = TimeInterval(duration)
$0.toValue = (toAngle.degrees)
$0.fromValue = (fromAngle.degrees)
$0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
$0.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
}
let fade = Init(CABasicAnimation(keyPath: "opacity")) {
let fade = customize(CABasicAnimation(keyPath: "opacity")) {
$0.duration = TimeInterval(duration)
$0.fromValue = fromOpacity
$0.toValue = toOpacity
$0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
$0.fillMode = kCAFillModeForwards
$0.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
$0.fillMode = CAMediaTimingFillMode.forwards
$0.isRemovedOnCompletion = false
}
let scale = Init(CABasicAnimation(keyPath: "transform.scale")) {
let scale = customize(CABasicAnimation(keyPath: "transform.scale")) {
$0.duration = TimeInterval(duration)
$0.toValue = toScale
$0.fromValue = fromScale
$0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
$0.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
}
view.layer.add(rotation, forKey: nil)
@@ -442,22 +482,22 @@ open class CircleMenu: UIButton {
fileprivate func hideCenterButton(duration: Double, delay: Double = 0) {
UIView.animate(withDuration: TimeInterval(duration), delay: TimeInterval(delay),
options: UIViewAnimationOptions.curveEaseOut,
options: UIView.AnimationOptions.curveEaseOut,
animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
self.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
}, completion: nil)
}
fileprivate func showCenterButton(duration: Float, delay: Double) {
UIView.animate(withDuration: TimeInterval(duration), delay: TimeInterval(delay), usingSpringWithDamping: 0.78,
initialSpringVelocity: 0, options: UIViewAnimationOptions.curveLinear,
initialSpringVelocity: 0, options: UIView.AnimationOptions.curveLinear,
animations: { () -> Void in
self.transform = CGAffineTransform(scaleX: 1, y: 1)
self.alpha = 1
},
self.transform = CGAffineTransform(scaleX: 1, y: 1)
self.alpha = 1
},
completion: nil)
let rotation = Init(CASpringAnimation(keyPath: "transform.rotation")) {
let rotation = customize(CASpringAnimation(keyPath: "transform.rotation")) {
$0.duration = TimeInterval(1.5)
$0.toValue = 0
$0.fromValue = (Float(-180).degrees)
@@ -466,19 +506,19 @@ open class CircleMenu: UIButton {
$0.beginTime = CACurrentMediaTime() + delay
}
let fade = Init(CABasicAnimation(keyPath: "opacity")) {
let fade = customize(CABasicAnimation(keyPath: "opacity")) {
$0.duration = TimeInterval(0.01)
$0.toValue = 0
$0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
$0.fillMode = kCAFillModeForwards
$0.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
$0.fillMode = CAMediaTimingFillMode.forwards
$0.isRemovedOnCompletion = false
$0.beginTime = CACurrentMediaTime() + delay
}
let show = Init(CABasicAnimation(keyPath: "opacity")) {
let show = customize(CABasicAnimation(keyPath: "opacity")) {
$0.duration = TimeInterval(duration)
$0.toValue = 1
$0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
$0.fillMode = kCAFillModeForwards
$0.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
$0.fillMode = CAMediaTimingFillMode.forwards
$0.isRemovedOnCompletion = false
$0.beginTime = CACurrentMediaTime() + delay
}
@@ -60,7 +60,7 @@ internal class CircleMenuButton: UIButton {
// MARK: configure
fileprivate func createContainer(_ size: CGSize, platform: UIView) -> UIView {
let container = Init(UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: size))) {
let container = customize(UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: size))) {
$0.backgroundColor = UIColor.clear
$0.translatesAutoresizingMaskIntoConstraints = false
$0.layer.anchorPoint = CGPoint(x: 0.5, y: 1)
@@ -117,7 +117,7 @@ internal class CircleMenuButton: UIButton {
UIView.animate(
withDuration: duration,
delay: delay,
options: UIViewAnimationOptions(),
options: UIView.AnimationOptions(),
animations: { () -> Void in
container.layer.transform = rotateTransform
},
@@ -132,7 +132,7 @@ internal class CircleMenuButton: UIButton {
internal extension CircleMenuButton {
internal func showAnimation(distance: Float, duration: Double, delay: Double = 0) {
func showAnimation(distance: Float, duration: Double, delay: Double = 0) {
guard let heightConstraint = (self.container?.constraints.filter { $0.identifier == "height" })?.first else {
fatalError()
}
@@ -148,7 +148,7 @@ internal extension CircleMenuButton {
delay: delay,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0,
options: UIViewAnimationOptions.curveLinear,
options: UIView.AnimationOptions.curveLinear,
animations: { () -> Void in
self.container?.superview?.layoutIfNeeded()
self.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
@@ -157,7 +157,7 @@ internal extension CircleMenuButton {
})
}
internal func hideAnimation(distance: Float, duration: Double, delay: Double = 0) {
func hideAnimation(distance: Float, duration: Double, delay: Double = 0) {
guard let heightConstraint = (self.container?.constraints.filter { $0.identifier == "height" })?.first else {
return
}
@@ -166,7 +166,7 @@ internal extension CircleMenuButton {
UIView.animate(
withDuration: duration,
delay: delay,
options: UIViewAnimationOptions.curveEaseIn,
options: UIView.AnimationOptions.curveEaseIn,
animations: { () -> Void in
self.container?.superview?.layoutIfNeeded()
self.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
@@ -179,7 +179,7 @@ internal extension CircleMenuButton {
})
}
internal func changeDistance(_ distance: CGFloat, animated _: Bool, duration: Double = 0, delay: Double = 0) {
func changeDistance(_ distance: CGFloat, animated _: Bool, duration: Double = 0, delay: Double = 0) {
guard let heightConstraint = (self.container?.constraints.filter { $0.identifier == "height" })?.first else {
fatalError()
@@ -190,7 +190,7 @@ internal extension CircleMenuButton {
UIView.animate(
withDuration: duration,
delay: delay,
options: UIViewAnimationOptions.curveEaseIn,
options: UIView.AnimationOptions.curveEaseIn,
animations: { () -> Void in
self.container?.superview?.layoutIfNeeded()
},
@@ -199,11 +199,11 @@ internal extension CircleMenuButton {
// MARK: layer animation
internal func rotationAnimation(_ angle: Float, duration: Double) {
let rotation = Init(CABasicAnimation(keyPath: "transform.rotation")) {
func rotationAnimation(_ angle: Float, duration: Double) {
let rotation = customize(CABasicAnimation(keyPath: "transform.rotation")) {
$0.duration = TimeInterval(duration)
$0.toValue = (angle.degrees)
$0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
$0.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
}
container?.layer.add(rotation, forKey: "rotation")
}
@@ -66,7 +66,7 @@ internal class CircleMenuLoader: UIView {
endAngle: CGFloat.pi * 2,
clockwise: true)
let circle = Init(CAShapeLayer()) {
let circle = customize(CAShapeLayer()) {
$0.path = circlePath.cgPath
$0.fillColor = UIColor.clear.cgColor
$0.strokeColor = color?.cgColor
@@ -81,7 +81,7 @@ internal class CircleMenuLoader: UIView {
translatesAutoresizingMaskIntoConstraints = false
// added constraints
let sizeConstraints = [NSLayoutAttribute.width, .height].map {
let sizeConstraints = [NSLayoutConstraint.Attribute.width, .height].map {
NSLayoutConstraint(item: self,
attribute: $0,
relatedBy: .equal,
@@ -92,7 +92,7 @@ internal class CircleMenuLoader: UIView {
}
addConstraints(sizeConstraints)
let centerConstaraints = [NSLayoutAttribute.centerY, .centerX].map {
let centerConstaraints = [NSLayoutConstraint.Attribute.centerY, .centerX].map {
NSLayoutConstraint(item: platform,
attribute: $0,
relatedBy: .equal,
@@ -105,7 +105,7 @@ internal class CircleMenuLoader: UIView {
}
internal func createRoundView(_ rect: CGRect, color: UIColor?) {
let roundView = Init(UIView(frame: rect)) {
let roundView = customize(UIView(frame: rect)) {
$0.backgroundColor = UIColor.black
$0.layer.cornerRadius = rect.size.width / 2.0
$0.backgroundColor = color
@@ -125,11 +125,11 @@ internal class CircleMenuLoader: UIView {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
let animation = Init(CABasicAnimation(keyPath: "strokeEnd")) {
let animation = customize(CABasicAnimation(keyPath: "strokeEnd")) {
$0.duration = CFTimeInterval(duration)
$0.fromValue = 0
$0.toValue = 1
$0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
$0.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
}
circle?.add(animation, forKey: nil)
CATransaction.commit()
@@ -137,12 +137,12 @@ internal class CircleMenuLoader: UIView {
internal func hideAnimation(_ duration: CGFloat, delay: Double, completion: @escaping () -> Void) {
let scale = Init(CABasicAnimation(keyPath: "transform.scale")) {
let scale = customize(CABasicAnimation(keyPath: "transform.scale")) {
$0.toValue = 1.2
$0.duration = CFTimeInterval(duration)
$0.fillMode = kCAFillModeForwards
$0.fillMode = CAMediaTimingFillMode.forwards
$0.isRemovedOnCompletion = false
$0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
$0.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
$0.beginTime = CACurrentMediaTime() + delay
}
layer.add(scale, forKey: nil)
@@ -150,7 +150,7 @@ internal class CircleMenuLoader: UIView {
UIView.animate(
withDuration: CFTimeInterval(duration),
delay: delay,
options: UIViewAnimationOptions.curveEaseIn,
options: UIView.AnimationOptions.curveEaseIn,
animations: { () -> Void in
self.alpha = 0
},
+1 -1
View File
@@ -60,7 +60,7 @@ class CircleMenuTests: XCTestCase {
circleMenu.onTap()
// when
circleMenu.buttonHandler((circleMenu.buttons?.first)!)
circleMenu.buttonHandler((circleMenu.buttons?.first)! as! CircleMenuButton)
// then
XCTAssertNil(circleMenu.buttons, "button is removed")
+42
View File
@@ -0,0 +1,42 @@
// swift-tools-version:5.1
//
// Package.swift
//
// Copyright (c) Ramotion (https://www.ramotion.com/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import PackageDescription
let package = Package(
name: "CircleMenu",
platforms: [
.iOS(.v9)
],
products: [
.library(name: "CircleMenu",
targets: ["CircleMenu"])
],
targets: [
.target(name: "CircleMenu",
path: "CircleMenuLib")
],
swiftLanguageVersions: [.v5]
)
+47 -34
View File
@@ -1,7 +1,27 @@
![header](./header.png)
![preview](./preview.gif)
<a href="https://www.ramotion.com/agency/app-development/?utm_source=gthb&utm_medium=repo&utm_campaign=circle-menu"><img src="https://github.com/Ramotion/circle-menu/blob/master/header.png"></a>
<a href="https://github.com/Ramotion/circle-menu">
<img align="left" src="https://github.com/Ramotion/circle-menu/blob/master/circle-menu.gif" width="480" height="360" /></a>
<p><h1 align="left">CIRCLE MENU</h1></p>
<h4>Simple, elegant UI menu with a circular layout and material design animations</h4>
___
<p><h6>We specialize in the designing and coding of custom UI for Mobile Apps and Websites.</h6>
<a href="https://www.ramotion.com/agency/app-development/?utm_source=gthb&utm_medium=repo&utm_campaign=circle-menu">
<img src="https://github.com/ramotion/gliding-collection/raw/master/contact_our_team@2x.png" width="187" height="34"></a>
</p>
<p><h6>Stay tuned for the latest updates:</h6>
<a href="https://goo.gl/rPFpid" >
<img src="https://i.imgur.com/ziSqeSo.png/" width="156" height="28"></a></p>
</br>
# CircleMenu
[![Twitter](https://img.shields.io/badge/Twitter-@Ramotion-blue.svg?style=flat)](http://twitter.com/Ramotion)
[![CocoaPods](https://img.shields.io/cocoapods/p/CircleMenu.svg)](https://cocoapods.org/pods/CircleMenu)
[![CocoaPods](https://img.shields.io/cocoapods/v/CircleMenu.svg)](http://cocoapods.org/pods/CircleMenu)
@@ -9,25 +29,8 @@
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Ramotion/circle-menu)
[![codebeat badge](https://codebeat.co/badges/6f67da5d-c416-4bac-9fb7-c2dc938feedc)](https://codebeat.co/projects/github-com-ramotion-circle-menu)
[![Travis](https://img.shields.io/travis/Ramotion/circle-menu.svg)](https://travis-ci.org/Ramotion/circle-menu)
[![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://paypal.me/Ramotion)
# Check this library on other platforms:
<a href="https://github.com/Ramotion/circle-menu-android">
<img src="https://github.com/ramotion/navigation-stack/raw/master/Android_Java@2x.png" width="178" height="81"></a>
<a href="https://github.com/Ramotion/react-native-circle-menu">
<img src="https://github.com/ramotion/navigation-stack/raw/master/React Native@2x.png" width="178" height="81"></a>
**Looking for developers for your project?**<br>
This project is maintained by Ramotion, Inc. We specialize in the designing and coding of custom UI for Mobile Apps and Websites.
<a href="https://ramotion.com/?utm_source=gthb&utm_medium=special&utm_campaign=circle-menu-contact-us/#Get_in_Touch">
<img src="https://github.com/ramotion/gliding-collection/raw/master/contact_our_team@2x.png" width="187" height="34"></a> <br>
The [iPhone mockup](https://store.ramotion.com?utm_source=gthb&utm_medium=special&utm_campaign=circle-menu) available [here](https://store.ramotion.com?utm_source=gthb&utm_medium=special&utm_campaign=circle-menu).
## Try this UI control in action
<a href="https://itunes.apple.com/app/apple-store/id1182360240?pt=550053&ct=gthb-circle-menu&mt=8" > <img src="https://github.com/Ramotion/navigation-stack/raw/master/Download_on_the_App_Store_Badge_US-UK_135x40.png" width="170" height="58"></a>
## Requirements
@@ -62,7 +65,7 @@ github "Ramotion/circle-menu"
func circleMenu(circleMenu: CircleMenu, willDisplay button: UIButton, atIndex: Int)
```
4) Use properties to confiure CircleMenu
4) Use properties to configure CircleMenu
```swift
@IBInspectable var buttonsCount: Int = 3
@@ -97,26 +100,36 @@ optional func circleMenu(circleMenu: CircleMenu, buttonWillSelected button: UIBu
// call after animation
optional func circleMenu(circleMenu: CircleMenu, buttonDidSelected button: UIButton, atIndex: Int)
// call upon cancel of the menu
// call upon cancel of the menu - fires immediately on button press
optional func menuCollapsed(circleMenu: CircleMenu)
// call upon opening of the menu - fires immediately on button press
optional func menuOpened(circleMenu: CircleMenu)
```
## Licence
## 🗂 Check this library on other language:
<a href="https://github.com/Ramotion/circle-menu-android">
<img src="https://github.com/ramotion/navigation-stack/raw/master/Android_Java@2x.png" width="178" height="81"></a>
<a href="https://github.com/Ramotion/react-native-circle-menu">
<img src="https://github.com/ramotion/navigation-stack/raw/master/React Native@2x.png" width="178" height="81"></a>
Circle menu is released under the MIT license.
## 📄 License
Circle Menu is released under the MIT license.
See [LICENSE](./LICENSE) for details.
<br>
# Get the Showroom App for iOS and Android to give it a try
Try this UI component and more like this in our mobile app. Contact us if interested.
This library is a part of a <a href="https://github.com/Ramotion/swift-ui-animation-components-and-libraries"><b>selection of our best UI open-source projects.</b></a>
<a href="https://itunes.apple.com/app/apple-store/id1182360240?pt=550053&ct=circle-menu&mt=8" >
If you use the open-source library in your project, please make sure to credit and backlink to www.ramotion.com
## 📱 Get the Showroom App for iOS to give it a try
Try this UI component and more like this in our iOS app. Contact us if interested.
<a href="https://itunes.apple.com/app/apple-store/id1182360240?pt=550053&ct=folding-cell&mt=8" >
<img src="https://github.com/ramotion/gliding-collection/raw/master/app_store@2x.png" width="117" height="34"></a>
<a href="https://ramotion.com/?utm_source=gthb&utm_medium=special&utm_campaign=circle-menu-contact-us/#Get_in_Touch">
<a href="https://www.ramotion.com/agency/app-development/?utm_source=gthb&utm_medium=repo&utm_campaign=circle-menu">
<img src="https://github.com/ramotion/gliding-collection/raw/master/contact_our_team@2x.png" width="187" height="34"></a>
<br>
<br>
Follow us for the latest updates<br>
[![Twitter URL](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=https://github.com/ramotion/circle-menu)
[![Twitter Follow](https://img.shields.io/twitter/follow/ramotion.svg?style=social)](https://twitter.com/ramotion)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 16 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB