34 Commits

Author SHA1 Message Date
Haik Aslanyan 63a209fb0c Update README.md 2020-06-23 21:41:58 +04:00
Haik Aslanyan 2fa33d0e88 updated readme 2020-05-09 13:30:36 +04:00
Haik Aslanyan 25d734d87d updated readme file 2020-05-07 21:19:32 +04:00
Haik Aslanyan a95fc1265c Merge pull request #7 from aslanyanhaik/develop
Develop
2020-05-07 21:11:29 +04:00
Haik Aslanyan e02e761c2e changed version number to 1.3.0 2020-05-07 21:10:48 +04:00
Haik Aslanyan 9e5ddf0082 updated example project 2020-05-07 21:10:04 +04:00
Haik Aslanyan aace0fd52d added ScanningMode parameter 2020-05-07 21:08:40 +04:00
Haik Aslanyan 63fd478b58 modified scanning logic for dark background images 2020-05-07 21:08:08 +04:00
Haik Aslanyan 783ac44550 moved decoding calculation into different queue 2020-05-07 17:07:24 +04:00
Haik Aslanyan 118dcd6752 Merge pull request #6 from aslanyanhaik/develop
Develop
2020-05-07 14:46:09 +04:00
Haik Aslanyan 05f71742a1 bumped to version 1.2.0 2020-05-07 14:45:25 +04:00
Haik Aslanyan d544551032 small change 2020-05-06 19:00:46 +04:00
Haik Aslanyan 255195d1b4 modified scanning area logic to support non centered images 2020-05-06 18:56:59 +04:00
Haik Aslanyan cbeea6ef44 removed redundant UIImagePickerControllerDelegate 2020-05-01 12:30:00 +04:00
Haik Aslanyan 3d584f7b23 updated example project 2020-04-30 21:50:34 +04:00
Haik Aslanyan e2a305dcf3 updated pixelThreshold range 2020-04-19 17:22:02 +04:00
Haik Aslanyan fb3fc03fe1 Merge pull request #4 from aslanyanhaik/develop
Develop
2020-04-19 16:56:00 +04:00
Haik Aslanyan 2e45c812b9 Merge branch 'develop' of https://github.com/aslanyanhaik/roundCode into develop 2020-04-19 16:53:45 +04:00
Haik Aslanyan 795bed4c5d updated to 1.1.1 version 2020-04-19 16:53:39 +04:00
Haik Aslanyan 843df78755 updated example project 2020-04-19 16:51:02 +04:00
Haik Aslanyan 5f88ff991e added color color validation for black background 2020-04-19 16:50:14 +04:00
Haik Aslanyan 395f608cee changed pixelThreshold from constant to range 2020-04-19 16:47:53 +04:00
Haik Aslanyan e58176cc85 Merge pull request #3 from aslanyanhaik/develop
Develop
2020-04-18 20:37:22 +04:00
Haik Aslanyan 5beb74e246 updated version to 1.1.0 2020-04-18 20:36:20 +04:00
Haik Aslanyan 5c5f9ed4a7 updated example project 2020-04-18 20:35:10 +04:00
Haik Aslanyan 7b1068c0a0 removed empty data check 2020-04-18 20:31:30 +04:00
Haik Aslanyan 3d2fb37f2c refactored RCImageDecoder 2020-04-18 20:31:04 +04:00
Haik Aslanyan 2d48af6823 added RCPointMapper 2020-04-18 20:29:01 +04:00
Haik Aslanyan 3e47bf2ba5 refactored RCCoderConfiguration 2020-04-17 12:01:47 +04:00
Haik Aslanyan a3777c040a updated example project 2020-04-16 14:29:23 +04:00
Haik Aslanyan 65c615304a updated screenshot 2020-04-15 17:25:12 +04:00
Haik Aslanyan bd1b7573a3 fixed constraint ambiguity 2020-04-15 15:45:55 +04:00
Haik Aslanyan e43603db6c fixed padding 2020-04-15 15:32:17 +04:00
Haik Aslanyan 8604ccca2e added example project 2020-04-15 15:31:33 +04:00
22 changed files with 1186 additions and 138 deletions
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
+365
View File
@@ -0,0 +1,365 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
22C936D02447221100128DE6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C936CF2447221100128DE6 /* AppDelegate.swift */; };
22C936D22447221100128DE6 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C936D12447221100128DE6 /* SceneDelegate.swift */; };
22C936D42447221100128DE6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C936D32447221100128DE6 /* ViewController.swift */; };
22C936D72447221100128DE6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 22C936D52447221100128DE6 /* Main.storyboard */; };
22C936D92447221200128DE6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 22C936D82447221200128DE6 /* Assets.xcassets */; };
22C936DC2447221200128DE6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 22C936DA2447221200128DE6 /* LaunchScreen.storyboard */; };
22C936E52447225600128DE6 /* RoundCode in Frameworks */ = {isa = PBXBuildFile; productRef = 22C936E42447225600128DE6 /* RoundCode */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
22C936CC2447221100128DE6 /* Round Code.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Round Code.app"; sourceTree = BUILT_PRODUCTS_DIR; };
22C936CF2447221100128DE6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
22C936D12447221100128DE6 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
22C936D32447221100128DE6 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
22C936D62447221100128DE6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
22C936D82447221200128DE6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
22C936DB2447221200128DE6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
22C936DD2447221200128DE6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
22C936C92447221100128DE6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
22C936E52447225600128DE6 /* RoundCode in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
22C936C32447221100128DE6 = {
isa = PBXGroup;
children = (
22C936CE2447221100128DE6 /* Example */,
22C936CD2447221100128DE6 /* Products */,
22C936E32447225600128DE6 /* Frameworks */,
);
sourceTree = "<group>";
};
22C936CD2447221100128DE6 /* Products */ = {
isa = PBXGroup;
children = (
22C936CC2447221100128DE6 /* Round Code.app */,
);
name = Products;
sourceTree = "<group>";
};
22C936CE2447221100128DE6 /* Example */ = {
isa = PBXGroup;
children = (
22C936CF2447221100128DE6 /* AppDelegate.swift */,
22C936D12447221100128DE6 /* SceneDelegate.swift */,
22C936D32447221100128DE6 /* ViewController.swift */,
22C936D52447221100128DE6 /* Main.storyboard */,
22C936D82447221200128DE6 /* Assets.xcassets */,
22C936DA2447221200128DE6 /* LaunchScreen.storyboard */,
22C936DD2447221200128DE6 /* Info.plist */,
);
path = Example;
sourceTree = "<group>";
};
22C936E32447225600128DE6 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
22C936CB2447221100128DE6 /* Example */ = {
isa = PBXNativeTarget;
buildConfigurationList = 22C936E02447221200128DE6 /* Build configuration list for PBXNativeTarget "Example" */;
buildPhases = (
22C936C82447221100128DE6 /* Sources */,
22C936C92447221100128DE6 /* Frameworks */,
22C936CA2447221100128DE6 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = Example;
packageProductDependencies = (
22C936E42447225600128DE6 /* RoundCode */,
);
productName = Example;
productReference = 22C936CC2447221100128DE6 /* Round Code.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
22C936C42447221100128DE6 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1140;
LastUpgradeCheck = 1140;
ORGANIZATIONNAME = "Haik Aslanyan";
TargetAttributes = {
22C936CB2447221100128DE6 = {
CreatedOnToolsVersion = 11.4;
};
};
};
buildConfigurationList = 22C936C72447221100128DE6 /* Build configuration list for PBXProject "Example" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 22C936C32447221100128DE6;
productRefGroup = 22C936CD2447221100128DE6 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
22C936CB2447221100128DE6 /* Example */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
22C936CA2447221100128DE6 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
22C936DC2447221200128DE6 /* LaunchScreen.storyboard in Resources */,
22C936D92447221200128DE6 /* Assets.xcassets in Resources */,
22C936D72447221100128DE6 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
22C936C82447221100128DE6 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
22C936D42447221100128DE6 /* ViewController.swift in Sources */,
22C936D02447221100128DE6 /* AppDelegate.swift in Sources */,
22C936D22447221100128DE6 /* SceneDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
22C936D52447221100128DE6 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
22C936D62447221100128DE6 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
22C936DA2447221200128DE6 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
22C936DB2447221200128DE6 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
22C936DE2447221200128DE6 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
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_DOCUMENTATION_COMMENTS = YES;
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;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
22C936DF2447221200128DE6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
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_DOCUMENTATION_COMMENTS = YES;
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;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
22C936E12447221200128DE6 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Example/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.roundcode.example;
PRODUCT_NAME = "Round Code";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
22C936E22447221200128DE6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Example/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.roundcode.example;
PRODUCT_NAME = "Round Code";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
22C936C72447221100128DE6 /* Build configuration list for PBXProject "Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
22C936DE2447221200128DE6 /* Debug */,
22C936DF2447221200128DE6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
22C936E02447221200128DE6 /* Build configuration list for PBXNativeTarget "Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
22C936E12447221200128DE6 /* Debug */,
22C936E22447221200128DE6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCSwiftPackageProductDependency section */
22C936E42447225600128DE6 /* RoundCode */ = {
isa = XCSwiftPackageProductDependency;
productName = RoundCode;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 22C936C42447221100128DE6 /* Project object */;
}
+51
View File
@@ -0,0 +1,51 @@
// MIT License
// Copyright (c) 2020 Haik Aslanyan
// 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 UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
@@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
+141
View File
@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="xzF-bb-zO0">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="a2g-4i-pyL">
<objects>
<viewController id="0zP-ee-W0J" customClass="ViewController" customModule="Round_Code" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Rvj-Nv-Ukh">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="IDN-sg-2ib">
<rect key="frame" x="20" y="129" width="374" height="374"/>
<constraints>
<constraint firstAttribute="width" secondItem="IDN-sg-2ib" secondAttribute="height" multiplier="1:1" id="mKx-mn-H2a"/>
</constraints>
</imageView>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Message" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="KyC-Sh-W2M">
<rect key="frame" x="5" y="503" width="404" height="27"/>
<constraints>
<constraint firstAttribute="height" constant="27" id="BZz-IP-j2V"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no"/>
<connections>
<action selector="didType:" destination="0zP-ee-W0J" eventType="editingChanged" id="Efh-0Z-Lqo"/>
<outlet property="delegate" destination="0zP-ee-W0J" id="ZBB-tX-bvC"/>
</connections>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="akC-qR-hPk">
<rect key="frame" x="20" y="129" width="374" height="374"/>
<connections>
<action selector="setImage:" destination="0zP-ee-W0J" eventType="touchUpInside" id="czy-4J-2pc"/>
</connections>
</button>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="zZa-y7-66Y">
<rect key="frame" x="20" y="93" width="374" height="32"/>
<segments>
<segment title="Orange"/>
<segment title="Red"/>
<segment title="Green"/>
<segment title="Brown"/>
<segment title="Cyan"/>
</segments>
<connections>
<action selector="setColor:" destination="0zP-ee-W0J" eventType="valueChanged" id="ULi-2Q-VAM"/>
</connections>
</segmentedControl>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pan-9N-son">
<rect key="frame" x="0.0" y="88" width="414" height="774"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<fontDescription key="fontDescription" type="system" pointSize="32"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="aXm-cj-1eD" firstAttribute="trailing" secondItem="zZa-y7-66Y" secondAttribute="trailing" constant="20" id="5cA-OR-hh7"/>
<constraint firstItem="akC-qR-hPk" firstAttribute="width" secondItem="IDN-sg-2ib" secondAttribute="width" id="603-Vl-61F"/>
<constraint firstItem="aXm-cj-1eD" firstAttribute="trailing" secondItem="pan-9N-son" secondAttribute="trailing" id="9oj-QO-fMG"/>
<constraint firstItem="zZa-y7-66Y" firstAttribute="top" secondItem="aXm-cj-1eD" secondAttribute="top" constant="5" id="AoR-LT-Gch"/>
<constraint firstItem="IDN-sg-2ib" firstAttribute="centerX" secondItem="Rvj-Nv-Ukh" secondAttribute="centerX" id="EaP-Hv-Hz2"/>
<constraint firstItem="KyC-Sh-W2M" firstAttribute="leading" secondItem="aXm-cj-1eD" secondAttribute="leading" constant="5" id="Hkf-Qo-pLm"/>
<constraint firstItem="IDN-sg-2ib" firstAttribute="leading" secondItem="aXm-cj-1eD" secondAttribute="leading" constant="20" id="HpO-I0-NCL"/>
<constraint firstItem="akC-qR-hPk" firstAttribute="height" secondItem="IDN-sg-2ib" secondAttribute="height" id="KuT-uW-Bnc"/>
<constraint firstItem="IDN-sg-2ib" firstAttribute="top" secondItem="zZa-y7-66Y" secondAttribute="bottom" constant="5" id="RoW-mq-u55"/>
<constraint firstItem="zZa-y7-66Y" firstAttribute="leading" secondItem="aXm-cj-1eD" secondAttribute="leading" constant="20" id="ZgL-Z2-tgR"/>
<constraint firstItem="IDN-sg-2ib" firstAttribute="trailing" secondItem="aXm-cj-1eD" secondAttribute="trailing" constant="-20" id="ayw-8g-tlm"/>
<constraint firstItem="KyC-Sh-W2M" firstAttribute="centerX" secondItem="Rvj-Nv-Ukh" secondAttribute="centerX" id="moY-9V-bbk"/>
<constraint firstItem="aXm-cj-1eD" firstAttribute="bottom" secondItem="pan-9N-son" secondAttribute="bottom" id="pCj-pu-ong"/>
<constraint firstItem="akC-qR-hPk" firstAttribute="centerY" secondItem="IDN-sg-2ib" secondAttribute="centerY" id="uaL-Io-awN"/>
<constraint firstItem="pan-9N-son" firstAttribute="leading" secondItem="aXm-cj-1eD" secondAttribute="leading" id="wYb-6N-3CK"/>
<constraint firstItem="KyC-Sh-W2M" firstAttribute="top" secondItem="IDN-sg-2ib" secondAttribute="bottom" id="xdH-CM-DTh"/>
<constraint firstItem="pan-9N-son" firstAttribute="top" secondItem="aXm-cj-1eD" secondAttribute="top" id="xxe-7M-C1w"/>
<constraint firstItem="akC-qR-hPk" firstAttribute="centerX" secondItem="IDN-sg-2ib" secondAttribute="centerX" id="yBy-TJ-2Y7"/>
</constraints>
<viewLayoutGuide key="safeArea" id="aXm-cj-1eD"/>
</view>
<navigationItem key="navigationItem" id="XgG-ZM-i9d">
<leftBarButtonItems>
<barButtonItem title="Share" id="so2-d3-VCF">
<connections>
<action selector="share:" destination="0zP-ee-W0J" id="uNU-bV-PaS"/>
</connections>
</barButtonItem>
<barButtonItem title="Config" id="33c-Ng-bhx">
<connections>
<action selector="changeConfig:" destination="0zP-ee-W0J" id="izA-qU-8it"/>
</connections>
</barButtonItem>
</leftBarButtonItems>
<rightBarButtonItems>
<barButtonItem title="Camera" id="Ckh-RB-Kad">
<connections>
<action selector="scan:" destination="0zP-ee-W0J" id="h78-sA-B0C"/>
</connections>
</barButtonItem>
<barButtonItem title="Image" id="rwW-La-cl3">
<connections>
<action selector="scanImage:" destination="0zP-ee-W0J" id="Cdh-9Q-DD2"/>
</connections>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
<connections>
<outlet property="imageView" destination="IDN-sg-2ib" id="V1v-sS-seZ"/>
<outlet property="messageLabel" destination="pan-9N-son" id="mQD-Ag-JzI"/>
<outlet property="textField" destination="KyC-Sh-W2M" id="6ZV-gl-0Oq"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Fmj-W7-iXK" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3992.8000000000002" y="1049.3253373313344"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="lJm-py-Mqj">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="xzF-bb-zO0" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="ryo-Z5-nM2">
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="0zP-ee-W0J" kind="relationship" relationship="rootViewController" id="mY9-sh-m1Z"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ZPe-rv-4ch" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3050" y="1050"/>
</scene>
</scenes>
</document>
+70
View File
@@ -0,0 +1,70 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppleMusicUsageDescription</key>
<string>To scan roundcodes</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>To save RoundCode images</string>
<key>NSCameraUsageDescription</key>
<string>To scan roundcodes</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
</dict>
</plist>
+67
View File
@@ -0,0 +1,67 @@
// MIT License
// Copyright (c) 2020 Haik Aslanyan
// 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 UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}
+159
View File
@@ -0,0 +1,159 @@
// MIT License
// Copyright (c) 2020 Haik Aslanyan
// 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 UIKit
import RoundCode
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var messageLabel: UILabel!
var coder = RCCoder()
var image = RCImage(message: "")
let colors: [[UIColor]] = [[.orange, .magenta], [.systemPink, .systemTeal], [.systemTeal, .systemGreen], [.orange, .brown], [.purple, .cyan]]
var isScanningFromLibrary = false
}
extension ViewController {
@IBAction func changeConfig(_ sender: Any) {
textField.text = ""
image.message = ""
imageView.image = try? coder.encode(image)
let vc = UIAlertController(title: "Select Configuration", message: nil, preferredStyle: .actionSheet)
let uuidAction = UIAlertAction(title: "UUID", style: .default) { _ in
self.coder = RCCoder(configuration: .uuidConfiguration)
}
let numericAction = UIAlertAction(title: "Numeric", style: .default) { _ in
self.coder = RCCoder(configuration: .numericConfiguration)
}
let shortAction = UIAlertAction(title: "Short", style: .default) { _ in
self.coder = RCCoder(configuration: .shortConfiguration)
}
let defaultAction = UIAlertAction(title: "Default", style: .default) { _ in
self.coder = RCCoder(configuration: .defaultConfiguration)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
vc.addAction(uuidAction)
vc.addAction(numericAction)
vc.addAction(shortAction)
vc.addAction(defaultAction)
vc.addAction(cancelAction)
present(vc, animated: true)
}
@IBAction func scanImage(_ sender: Any) {
isScanningFromLibrary = true
let vc = UIImagePickerController()
vc.sourceType = .photoLibrary
vc.allowsEditing = true
vc.delegate = self
present(vc, animated: true)
}
@IBAction func scan(_ sender: Any) {
let vc = RCCameraViewController()
coder.scanningMode = .lightBackground
vc.coder = coder
vc.delegate = self
present(vc, animated: true)
}
@IBAction func share(_ sender: Any) {
image.size = 1000
image.contentInsets = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)
let uiImage = try? coder.encode(image)
image.size = 300
image.contentInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
let vc = UIActivityViewController.init(activityItems: [uiImage!], applicationActivities: nil)
present(vc, animated: true)
}
@IBAction func setImage(_ sender: Any) {
let vc = UIImagePickerController()
vc.sourceType = .photoLibrary
vc.allowsEditing = true
vc.delegate = self
present(vc, animated: true)
}
@IBAction func setColor(_ sender: UISegmentedControl) {
image.tintColors = colors[sender.selectedSegmentIndex]
imageView.image = try? coder.encode(image)
}
@IBAction func didType(_ sender: UITextField) {
image.message = sender.text ?? ""
imageView.image = try? coder.encode(image)
}
}
extension ViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return textField.resignFirstResponder()
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string) as String
let status = coder.validate(newString)
return status
}
}
extension ViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let camImage = info[.editedImage] as! UIImage
if isScanningFromLibrary {
if let message = try? coder.decode(camImage) {
messageLabel.text = message
messageLabel.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.messageLabel.isHidden = true
}
}
} else {
image.attachmentImage = camImage
imageView.image = try? coder.encode(image)
}
isScanningFromLibrary = false
picker.dismiss(animated: true)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
isScanningFromLibrary = false
picker.dismiss(animated: true)
}
}
extension ViewController: RCCameraViewControllerDelegate {
func cameraViewController(didFinishScanning message: String) {
messageLabel.text = message
messageLabel.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.messageLabel.isHidden = true
}
}
}
+14 -12
View File
@@ -5,10 +5,8 @@
[![Twitter: @aslanyanhaik](https://img.shields.io/badge/Contact-Twitter-blue.svg?style=flat)](https://twitter.com/aslanyanhaik)
RoundCode is a Facebook messenger like custom QR code with lots of customization.
In addition to encoder the RoundCode also includes convenient camera scanner and decoder.
RoundCode is custom circular QR code with lots of customization.
Similar to Facebook messenger and Apple's App Clip Codes the RoundCode includes convenient camera scanner and decoder.
<h3 align="center">
<img src="appearance.png" alt="Different styles of RoundCode for iOS"/>
@@ -81,14 +79,14 @@ You can also decode a UIImage like this
```swift
let coder = RCCoder()
do {
messageLabel.text = try coder.decode(UIImage(named: code)!)
} catch {
//handle errors
}
do {
messageLabel.text = try coder.decode(UIImage(named: code)!)
} catch {
//handle errors
}
```
### Appearance
## Appearance
You can change the appearance like this
@@ -100,10 +98,14 @@ image.size = 300
image.gradientType = .linear(angle: CGFloat.pi)
image.tintColors = [.red, .black]
```
If image is on dark background you should change scanning mode to `darkBackground`
⚠️ When choosing colors or transparent background you should keep in mind that decoder will detect the code on light background with dark colors ⚠️
```swift
let coder = RCCoder()
coder.scanningMode = .darkBackground
```
### Advanced
## Advanced coding configuration
You can provide custom coding configuration in order to encode long text by reducing number of characters
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'RoundCode'
s.version = '1.0.0'
s.version = '1.3.0'
s.summary = 'Facebook messenger style custom barcode.'
s.description = <<-DESC
Encode and decode data into custom stylish barcode.
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:">
</FileRef>
<FileRef
location = "group:Example/Example.xcodeproj">
</FileRef>
</Workspace>
@@ -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
View File
@@ -70,7 +70,6 @@ extension RCBitCoder {
func decode(_ bits: [RCBit]) throws -> String {
guard bits.contains(.one) else { return "" }
//starting character to bits
var startingIndexCharacter = String(repeating: "0", count: configuration.bitesPerSymbol)
startingIndexCharacter += String(configuration.characters.firstIndex(of: configuration.version.startingCharacter)!, radix: 2)
+2 -1
View File
@@ -28,5 +28,6 @@ struct RCConstants {
static let dotSizeScale: CGFloat = 0.08
static let dotPatterns: [CGFloat] = [6, 4, 2]
static let dotPointRange = (Float(1.3)...Float(2.5))
static let pixelThreshold = 180
static let lightBackgroundRange = UInt8(0)...UInt8(180)
static let darkBackgroundRange = UInt8(100)...UInt8(255)
}
+84 -106
View File
@@ -27,41 +27,24 @@ struct RCImageDecoder {
internal let configuration: RCCoderConfiguration
internal var size = 720
internal var bytesPerRow = 720
internal var padding = 0
private var sectionSize: Int {
self.size / 5
}
internal var pixelThreshold = RCConstants.lightBackgroundRange
}
extension RCImageDecoder {
func process(pointer: UnsafeMutablePointer<UInt8>) throws -> [RCBit] {
let bufferData = UnsafeMutableBufferPointer<UInt8>(start: pointer, count: size * bytesPerRow)
let data = PixelContainer(rows: bytesPerRow, items: bufferData)
var points = [CGPoint]()
for side in Side.allCases {
switch side {
case .left:
let point = try scanControlPoint(for: data, region: (padding, (size - sectionSize) / 2), side: side)
points.append(point)
case .top:
let point = try scanControlPoint(for: data, region: ((size - sectionSize) / 2, padding), side: side)
points.append(point)
case .right:
let point = try scanControlPoint(for: data, region: (size - sectionSize - padding, (size - sectionSize) / 2), side: side)
points.append(point)
case .bottom:
let point = try scanControlPoint(for: data, region: ((size - sectionSize) / 2, size - sectionSize - padding), side: side)
points.append(point)
}
}
let image = try fixPerspective(pointer, points: points)
let bits = decode(image)
let points = try scanControlPoints(for: data)
let transform = calculateTransform(from: points)
let mapper = RCPointMapper(transform: transform, size: size)
let locations = mapper.map(points: calculateBitLocations())
let bits = locations.map { pixelThreshold.contains(data[Int($0.x), Int($0.y)]) ? RCBit.one : RCBit.zero }
return bits
}
func decode(_ image: UIImage) throws -> [RCBit] {
let pixelData = UnsafeMutableRawPointer.allocate(byteCount: size * size, alignment: MemoryLayout<UInt8>.alignment)
let context = generateContext(data: pixelData, size: size, bytesPerRow: self.bytesPerRow)
let context = CGContext(data: pixelData, width: size, height: size, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceGray(), bitmapInfo: CGImageAlphaInfo.none.rawValue)
context?.draw(image.cgImage!, in: CGRect(origin: .zero, size: CGSize(width: size, height: size)))
let bits = try process(pointer: pixelData.assumingMemoryBound(to: UInt8.self))
pixelData.deallocate()
@@ -70,47 +53,66 @@ extension RCImageDecoder {
}
extension RCImageDecoder {
private func scanControlPoint(for data: PixelContainer, region: (x: Int, y: Int), side: Side) throws -> CGPoint {
func scan(region: (x: Int, y: Int, size: Int), data: PixelContainer, coordinate: (Int) -> (x: Int, y: Int), comparison: (PixelPattern, (x: Int, y: Int)) -> Bool) -> [PixelPattern] {
var lastPattern = PixelPattern(bit: data[region.x, region.y] > RCConstants.pixelThreshold ? RCBit.zero : RCBit.one, x: region.x, y: region.y, count: 0)
var pixelPatterns = [lastPattern]
var count = 0
let maxSize = region.size * region.size
while count < maxSize {
let coordinate = coordinate(count)
let bit = data[coordinate.x, coordinate.y] > RCConstants.pixelThreshold ? RCBit.zero : RCBit.one
if comparison(lastPattern, coordinate), lastPattern.bit == bit {
lastPattern.count += 1
pixelPatterns[pixelPatterns.count - 1] = lastPattern
} else {
lastPattern = PixelPattern(bit: bit, x: coordinate.x, y: coordinate.y, count: 1)
pixelPatterns.append(lastPattern)
}
count += 1
private func scanControlPoints(for data: PixelContainer) throws -> [CGPoint] {
var horizontalPatterns = [PixelPattern]()
var verticalPatterns = [PixelPattern]()
var points = [CGPoint]()
for side in Side.allCases {
switch side {
case .left:
horizontalPatterns = scanPixelPattern(for: .horizontal, data: data)
points.append(try controlPoint(for: horizontalPatterns, side: side))
case .right:
points.append(try controlPoint(for: horizontalPatterns, side: side))
case .top:
verticalPatterns = scanPixelPattern(for: .vertical, data: data)
points.append(try controlPoint(for: verticalPatterns, side: side))
case .bottom:
points.append(try controlPoint(for: verticalPatterns, side: side))
}
return pixelPatterns
}
let pixelPatterns: [PixelPattern]
switch side {
case .left, .right:
pixelPatterns = scan(region: (region.x, region.y, sectionSize), data: data, coordinate: { count in
let x = count % sectionSize + region.x
let y = count / sectionSize + region.y
return (x, y)
}, comparison: { (pattern, coordinate) in
return pattern.y == coordinate.y
})
case .top, .bottom:
pixelPatterns = scan(region: (region.x, region.y, sectionSize), data: data, coordinate: { count in
let x = count / sectionSize + region.x
let y = count % sectionSize + region.y
return (x, y)
}, comparison: { (pattern, coordinate) in
return pattern.x == coordinate.x
})
return points
}
private func scanPixelPattern(for mode: ScanMode, data: PixelContainer) -> [PixelPattern] {
var lastPattern = PixelPattern(bit: pixelThreshold.contains((data[0, 0])) ? RCBit.one : RCBit.zero, x: 0, y: 0, count: 0)
var pixelPatterns = [lastPattern]
var count = 0
let maxSize = size * size
switch mode {
case .horizontal:
while count < maxSize {
let x = count % size
let y = count / size
let bit = pixelThreshold.contains(data[x, y]) ? RCBit.one : RCBit.zero
if lastPattern.y == y, lastPattern.bit == bit {
lastPattern.count += 1
pixelPatterns[pixelPatterns.count - 1] = lastPattern
} else {
lastPattern = PixelPattern(bit: bit, x: x, y: y, count: 1)
pixelPatterns.append(lastPattern)
}
count += 1
}
case .vertical:
while count < maxSize {
let x = count / size
let y = count % size
let bit = pixelThreshold.contains(data[x, y]) ? RCBit.one : RCBit.zero
if lastPattern.x == x, lastPattern.bit == bit {
lastPattern.count += 1
pixelPatterns[pixelPatterns.count - 1] = lastPattern
} else {
lastPattern = PixelPattern(bit: bit, x: x, y: y, count: 1)
pixelPatterns.append(lastPattern)
}
count += 1
}
}
return pixelPatterns
}
private func controlPoint(for pixelPatterns: [PixelPattern], side: Side) throws -> CGPoint {
let controlPoints = try pixelPatterns.withUnsafeBufferPointer { (pixelPatternsBuffer) -> [CGPoint] in
var points = [CGPoint]()
guard pixelPatternsBuffer.count >= 5 else { throw RCError.decoding }
@@ -162,43 +164,22 @@ extension RCImageDecoder {
}
}.first!
}
private func fixPerspective(_ data: UnsafeMutablePointer<UInt8>, points: [CGPoint]) throws -> CGImage {
guard let context = generateContext(data: data, size: size, bytesPerRow: bytesPerRow), let cgImage = context.makeImage() else {
throw RCError.decoding
}
let image = UIGraphicsImageRenderer(size: CGSize(width: CGFloat(size), height: CGFloat(size))).image { context in
let baseView = UIView(frame: context.format.bounds)
let imageView = UIImageView(frame: context.format.bounds)
baseView.addSubview(imageView)
imageView.image = UIImage(cgImage: cgImage)
imageView.layer.anchorPoint = .zero
imageView.layer.frame = context.format.bounds
let perspective = RCTransformation(points)
let destination = RCTransformation([CGPoint(x: context.format.bounds.minX, y: context.format.bounds.midY),
CGPoint(x: context.format.bounds.midX, y: context.format.bounds.minY),
CGPoint(x: context.format.bounds.midX, y: context.format.bounds.maxY),
CGPoint(x: context.format.bounds.maxX, y: context.format.bounds.midY)])
let transform = perspective.perspectiveTransform(to: destination)
imageView.transform3D = transform
baseView.drawHierarchy(in: context.format.bounds, afterScreenUpdates: true)
}
guard let renderImage = image.cgImage else {
throw RCError.decoding
}
return renderImage
private func calculateTransform(from points: [CGPoint]) -> CATransform3D {
let perspective = RCTransformation(points)
let middleRect = CGRect(origin: .zero, size: CGSize(width: CGFloat(size), height: CGFloat(size)))
let destination = RCTransformation([CGPoint(x: middleRect.minX, y: middleRect.midY),
CGPoint(x: middleRect.midX, y: middleRect.minY),
CGPoint(x: middleRect.midX, y: middleRect.maxY),
CGPoint(x: middleRect.maxX, y: middleRect.midY)])
return perspective.perspectiveTransform(to: destination)
}
private func decode(_ image: CGImage) -> [RCBit] {
let pixelData = UnsafeMutableRawPointer.allocate(byteCount: image.width * image.height, alignment: MemoryLayout<UInt8>.alignment)
let context = generateContext(data: pixelData, size: image.width, bytesPerRow: image.width)
context?.draw(image, in: CGRect(origin: .zero, size: CGSize(width: image.width, height: image.height)))
let buffer = UnsafeMutableBufferPointer<UInt8>(start: pixelData.assumingMemoryBound(to: UInt8.self), count: image.width * image.height)
let data = PixelContainer(rows: image.height, items: buffer)
let size = CGFloat(data.columns)
let lineWidth = CGFloat(image.height) * RCConstants.dotSizeScale / 5 //number of lines including spaces
let mainRadius = CGFloat(image.height) / 2
let startAngle = asin(CGFloat(image.height) * RCConstants.dotSizeScale / mainRadius)
private func calculateBitLocations() -> [CGPoint] {
let size = CGFloat(self.size)
let lineWidth = size * RCConstants.dotSizeScale / 5 //number of lines including spaces
let mainRadius = size / 2
let startAngle = asin(size * RCConstants.dotSizeScale / mainRadius)
var points = [CGPoint]()
let center = CGPoint(x: size / 2, y: size / 2)
(0...3).forEach { offset in
@@ -214,15 +195,7 @@ extension RCImageDecoder {
}
}
}
let bits = points.map { data[Int($0.x), Int($0.y)] > RCConstants.pixelThreshold ? RCBit.zero : RCBit.one }
pixelData.deallocate()
return bits
}
}
extension RCImageDecoder {
private func generateContext(data: UnsafeMutableRawPointer?, size: Int, bytesPerRow: Int) -> CGContext? {
return CGContext(data: data, width: size, height: size, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceGray(), bitmapInfo: CGImageAlphaInfo.none.rawValue)
return points
}
}
@@ -242,6 +215,11 @@ extension RCImageDecoder {
case right
}
enum ScanMode {
case horizontal
case vertical
}
final class PixelContainer {
let rows: Int
var columns: Int { data.count / rows }
+55
View File
@@ -0,0 +1,55 @@
// MIT License
// Copyright (c) 2020 Haik Aslanyan
// 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 QuartzCore
struct RCPointMapper {
private var transform: CATransform3D
private let denominatorX: CGFloat
private let denominatorY: CGFloat
private let denominatorW: CGFloat
private let size: CGFloat
init(transform: CATransform3D, size: Int) {
let translateDueToDisposition = CATransform3DMakeTranslation(-CGFloat(size) / 2, -CGFloat(size) / 2, 0)
self.transform = CATransform3DConcat(transform, translateDueToDisposition)
self.size = CGFloat(size)
self.denominatorX = transform.m12 * transform.m21 - transform.m11 * transform.m22 + transform.m14 * transform.m22 * transform.m41 - transform.m12 * transform.m24 * transform.m41 - transform.m14 * transform.m21 * transform.m42 + transform.m11 * transform.m24 * transform.m42
self.denominatorY = -transform.m12 * transform.m21 + transform.m11 * transform.m22 - transform.m14 * transform.m22 * transform.m41 + transform.m12 * transform.m24 * transform.m41 + transform.m14 * transform.m21 * transform.m42 - transform.m11 * transform.m24 * transform.m42
self.denominatorW = transform.m12 * transform.m21 - transform.m11 * transform.m22 + transform.m14 * transform.m22 * transform.m41 - transform.m12 * transform.m24 * transform.m41 - transform.m14 * transform.m21 * transform.m42 + transform.m11 * transform.m24 * transform.m42
}
func map(points: [CGPoint]) -> [CGPoint] {
return points.map { point in
let modelPoint = CGPoint(x: (point.x * 2.0 - size) / 2.0, y: (point.y * 2.0 - size) / 2.0)
let x = modelPoint.x
let y = modelPoint.y
let xp: CGFloat = (transform.m22 * transform.m41 - transform.m21 * transform.m42 - transform.m22 * x + transform.m24 * transform.m42 * x + transform.m21 * y - transform.m24 * transform.m41 * y) / denominatorX
let yp: CGFloat = (-transform.m11 * transform.m42 + transform.m12 * (transform.m41 - x) + transform.m14 * transform.m42 * x + transform.m11 * y - transform.m14 * transform.m41 * y) / denominatorY
let wp: CGFloat = (transform.m12 * transform.m21 - transform.m11 * transform.m22 + transform.m14 * transform.m22 * x - transform.m12 * transform.m24 * x - transform.m14 * transform.m21 * y + transform.m11 * transform.m24 * y) / denominatorW
let actualPoint = CGPoint(x: xp / wp, y: yp / wp)
let isOutOfBounds = !(0...size).contains(actualPoint.x) || !(0...size).contains(actualPoint.y)
return isOutOfBounds ? .zero : actualPoint
}
}
}
+8 -13
View File
@@ -23,7 +23,7 @@
import UIKit
import AVFoundation
public final class RCCameraViewController: UIViewController, UIImagePickerControllerDelegate {
public final class RCCameraViewController: UIViewController {
//MARK: Public properties
public weak var delegate: RCCameraViewControllerDelegate?
@@ -42,8 +42,9 @@ public final class RCCameraViewController: UIViewController, UIImagePickerContro
private var circleLayer = CAShapeLayer()
private lazy var cameraView: UIView = {
let cameraView = UIView()
cameraView.backgroundColor = .black
cameraView.backgroundColor = .clear
self.view.addSubview(cameraView)
cameraView.translatesAutoresizingMaskIntoConstraints = false
cameraView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
cameraView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true
cameraView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
@@ -112,7 +113,6 @@ public extension RCCameraViewController {
super.viewDidLayoutSubviews()
configureMaskLayer()
videoPreviewLayer?.frame = view.bounds
calculateScanArea()
}
}
@@ -156,12 +156,11 @@ extension RCCameraViewController {
guard let captureDevice = AVCaptureDevice.default(for: .video) else { return }
do {
captureSession.sessionPreset = .hd1280x720
calculateScanArea()
let input = try AVCaptureDeviceInput(device: captureDevice)
input.device.activeVideoMaxFrameDuration = CMTimeMake(value: 1, timescale: 30)
captureSession.addInput(input)
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: .main)
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global(qos: .userInteractive))
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)]
captureSession.addOutput(videoOutput)
} catch {
@@ -169,12 +168,6 @@ extension RCCameraViewController {
}
}
private func calculateScanArea() {
let actualWidth = view.frame.height / 16 * 9
let sideArea = (actualWidth - view.frame.width * 0.9) / 2
coder.imageDecoder.padding = Int(sideArea / actualWidth * 720)
}
private func configureVideoPreview(orientation: AVCaptureVideoOrientation = .portrait) {
videoPreviewLayer?.removeFromSuperlayer()
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
@@ -204,8 +197,10 @@ extension RCCameraViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
coder.imageDecoder.bytesPerRow = bytesPerRow
if let message = try? coder.decode(buffer: lumaCopy.assumingMemoryBound(to: UInt8.self)) {
captureSession.stopRunning()
delegate?.cameraViewController(didFinishScanning: message)
dismiss(animated: true)
DispatchQueue.main.async {[weak self] in
self?.delegate?.cameraViewController(didFinishScanning: message)
self?.dismiss(animated: true)
}
}
lumaCopy.deallocate()
CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly)
+13 -1
View File
@@ -25,6 +25,11 @@ import UIKit
public final class RCCoder {
public let configuration: RCCoderConfiguration
public var scanningMode = ScanningMode.lightBackground {
didSet {
imageDecoder.pixelThreshold = scanningMode == .lightBackground ? RCConstants.lightBackgroundRange : RCConstants.darkBackgroundRange
}
}
internal lazy var imageDecoder = RCImageDecoder(configuration: self.configuration)
internal lazy var imageEncoder = RCImageEncoder(configuration: self.configuration)
internal lazy var bitCoder = RCBitCoder(configuration: self.configuration)
@@ -44,7 +49,6 @@ public extension RCCoder {
func decode(_ image: UIImage) throws -> String {
guard image.size.width == image.size.height else { throw RCError.wrongImageSize }
imageDecoder.size = image.cgImage!.height
imageDecoder.padding = 0
imageDecoder.bytesPerRow = image.cgImage!.height
let bits = try imageDecoder.decode(image)
let message = try bitCoder.decode(bits)
@@ -56,6 +60,13 @@ public extension RCCoder {
}
}
public extension RCCoder {
enum ScanningMode {
case lightBackground
case darkBackground
}
}
extension RCCoder {
func decode(buffer: UnsafeMutablePointer<UInt8>) throws -> String {
@@ -64,3 +75,4 @@ extension RCCoder {
return message
}
}
+2 -3
View File
@@ -24,13 +24,12 @@ import Foundation
public struct RCCoderConfiguration {
public let version: Version
public let version = Version.v1
public let maxMessageCount: Int
public let bitesPerSymbol: Int
public let characters: [Character]
public init(version: Version = .v1, characters: String) {
self.version = version
public init(characters: String) {
var charactersArray = characters.map({$0})
charactersArray.append(version.startingCharacter)
charactersArray.append(contentsOf: version.emptyCharacters)
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 539 KiB

After

Width:  |  Height:  |  Size: 598 KiB