Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85462bf513 | |||
| 77300f8afa | |||
| 04f51a0069 | |||
| 7b02dd484c | |||
| 8ac39b1dd7 | |||
| 79b4d4a0d0 | |||
| 3d7ffe23f5 | |||
| 445c5d424f | |||
| 7289830f99 | |||
| 7feb720b6f | |||
| f67a0b539c | |||
| 5dc8b23b40 | |||
| 0e27171ed0 | |||
| b1b8a5ca13 | |||
| be91a02ba1 | |||
| f36c02a228 | |||
| dc44eaec33 | |||
| df7816c3d6 | |||
| efeb94c3eb | |||
| 653daed11c | |||
| e0867b9d04 | |||
| a5414b2031 |
@@ -0,0 +1 @@
|
||||
Example/Pods/* linguist-vendored
|
||||
@@ -0,0 +1 @@
|
||||
4.0
|
||||
+2
-2
@@ -3,12 +3,12 @@
|
||||
# * https://github.com/supermarin/xcpretty#usage
|
||||
|
||||
language: objective-c
|
||||
osx_image: xcode8
|
||||
osx_image: xcode9
|
||||
# cache: cocoapods
|
||||
# podfile: Example/Podfile
|
||||
before_install:
|
||||
- gem install cocoapods # Since Travis is not always on latest version
|
||||
# - pod install --project-directory=Example
|
||||
script:
|
||||
- set -o pipefail && xcodebuild -workspace Example/ScalingCarousel.xcworkspace -scheme ScalingCarousel-Example -sdk iphonesimulator -destination 'platform=iOS Simulator,OS=10.0,name=iPhone 7' ONLY_ACTIVE_ARCH=NO | xcpretty
|
||||
- set -o pipefail && xcodebuild -workspace Example/ScalingCarousel.xcworkspace -scheme ScalingCarousel-Example -sdk iphonesimulator -destination 'platform=iOS Simulator,OS=11.0,name=iPhone 7' ONLY_ACTIVE_ARCH=NO | xcpretty
|
||||
- pod lib lint
|
||||
|
||||
+41
-4
@@ -139,9 +139,9 @@
|
||||
2AF1556C49363C2BA1C10796E1C93F58 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BA88835C545DBDE8A6A6984912DA1E88 /* ScalingCarouselView.swift */,
|
||||
0F029A4B46297C4E659645069518366C /* ScalingCarouselCell.swift */,
|
||||
BFA8691AE35E197F6C1CFAD0EC691EE2 /* ScalingCarouselLayout.swift */,
|
||||
BA88835C545DBDE8A6A6984912DA1E88 /* ScalingCarouselView.swift */,
|
||||
);
|
||||
path = Classes;
|
||||
sourceTree = "<group>";
|
||||
@@ -317,7 +317,15 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0730;
|
||||
LastUpgradeCheck = 0700;
|
||||
LastUpgradeCheck = 0900;
|
||||
TargetAttributes = {
|
||||
479882F85D2E213BDCC80F17AAAA8A63 = {
|
||||
LastSwiftMigration = "";
|
||||
};
|
||||
E9582E7E712D7CD7A32DF86E8120CBC5 = {
|
||||
LastSwiftMigration = 0900;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
@@ -382,6 +390,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 2CAFE40C0C16F348A480291448686E57 /* Pods-ScalingCarousel_Example.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||
@@ -407,6 +416,8 @@
|
||||
PRODUCT_NAME = Pods_ScalingCarousel_Example;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = On;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -473,7 +484,8 @@
|
||||
PRODUCT_NAME = ScalingCarousel;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -489,19 +501,29 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = 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_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGNING_REQUIRED = NO;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"POD_CONFIGURATION_RELEASE=1",
|
||||
"$(inherited)",
|
||||
@@ -515,6 +537,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/;
|
||||
STRIP_INSTALLED_PRODUCT = NO;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SYMROOT = "${SRCROOT}/../build";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
@@ -529,20 +552,30 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = 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_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGNING_REQUIRED = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"POD_CONFIGURATION_DEBUG=1",
|
||||
@@ -568,6 +601,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 6820F178C5F5E9638CACA2814BABA73E /* Pods-ScalingCarousel_Example.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||
@@ -594,6 +628,8 @@
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = On;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -626,7 +662,8 @@
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
|
||||
+20
-20
@@ -2,25 +2,25 @@
|
||||
<!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>en</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>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</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>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -245,16 +245,16 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0720;
|
||||
LastUpgradeCheck = 0720;
|
||||
LastUpgradeCheck = 0900;
|
||||
ORGANIZATIONNAME = CocoaPods;
|
||||
TargetAttributes = {
|
||||
607FACCF1AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
LastSwiftMigration = 0820;
|
||||
LastSwiftMigration = 0900;
|
||||
};
|
||||
607FACE41AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
LastSwiftMigration = 0820;
|
||||
LastSwiftMigration = 0900;
|
||||
TestTargetID = 607FACCF1AFB9204008FA782;
|
||||
};
|
||||
};
|
||||
@@ -448,13 +448,21 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = 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_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_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -493,13 +501,21 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = 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_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_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -518,6 +534,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
@@ -526,6 +543,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 54ACDAF6D64CCC3389CF9744 /* Pods-ScalingCarousel_Example.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = ScalingCarousel/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
@@ -533,7 +551,8 @@
|
||||
MODULE_NAME = ExampleApp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -541,6 +560,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = C076B0D8341FA770E7E1424B /* Pods-ScalingCarousel_Example.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = ScalingCarousel/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
@@ -548,7 +568,8 @@
|
||||
MODULE_NAME = ExampleApp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -568,7 +589,8 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -584,7 +606,8 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0720"
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -40,6 +40,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
@@ -69,6 +70,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="jed-5q-J52">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.19" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="jed-5q-J52">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.16"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@@ -74,19 +75,29 @@
|
||||
<action selector="showHideButtonPressed:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="Jsy-vc-cWp"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UYO-rO-XuZ">
|
||||
<rect key="frame" x="187" y="143" width="0.0" height="0.0"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Mq8-SH-Mx8" firstAttribute="width" secondItem="kh9-bI-dsS" secondAttribute="width" id="4Ux-ib-2uZ"/>
|
||||
<constraint firstItem="UYO-rO-XuZ" firstAttribute="centerX" secondItem="KAz-De-GJ8" secondAttribute="centerX" id="8U2-jX-Aw6"/>
|
||||
<constraint firstItem="KAz-De-GJ8" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="85" id="Ffh-uk-Qnl"/>
|
||||
<constraint firstItem="2fi-mo-0CV" firstAttribute="top" secondItem="Mq8-SH-Mx8" secondAttribute="bottom" constant="15" id="Kee-s5-jSC"/>
|
||||
<constraint firstItem="KAz-De-GJ8" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="M5E-lE-5Pp"/>
|
||||
<constraint firstItem="Mq8-SH-Mx8" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="NPp-qY-rgM"/>
|
||||
<constraint firstItem="UYO-rO-XuZ" firstAttribute="top" secondItem="KAz-De-GJ8" secondAttribute="bottom" constant="8" id="nfn-SZ-h6K"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Storyboard" id="7Ol-Hv-b1V"/>
|
||||
<connections>
|
||||
<outlet property="carousel" destination="Mq8-SH-Mx8" id="PYK-cv-2wb"/>
|
||||
<outlet property="carouselBottomConstraint" destination="Kee-s5-jSC" id="HnZ-jP-gpN"/>
|
||||
<outlet property="output" destination="UYO-rO-XuZ" id="v8Z-xt-lyT"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
|
||||
@@ -118,6 +129,7 @@
|
||||
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="jed-5q-J52" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<tabBar key="tabBar" contentMode="scaleToFill" id="Xg8-GK-uv2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</tabBar>
|
||||
|
||||
@@ -24,6 +24,9 @@ class CodeCell: ScalingCarouselCell {
|
||||
}
|
||||
|
||||
class CodeViewController: UIViewController {
|
||||
|
||||
// MARK: - Properties (Private)
|
||||
fileprivate var scalingCarousel: ScalingCarouselView!
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
@@ -43,8 +46,9 @@ class CodeViewController: UIViewController {
|
||||
private func addCarousel() {
|
||||
|
||||
let frame = CGRect(x: 0, y: 0, width: 0, height: 0)
|
||||
let scalingCarousel = ScalingCarouselView(withFrame: frame, andInset: 20)
|
||||
scalingCarousel = ScalingCarouselView(withFrame: frame, andInset: 20)
|
||||
scalingCarousel.dataSource = self
|
||||
scalingCarousel.delegate = self
|
||||
scalingCarousel.translatesAutoresizingMaskIntoConstraints = false
|
||||
scalingCarousel.backgroundColor = .white
|
||||
|
||||
@@ -76,3 +80,10 @@ extension CodeViewController: UICollectionViewDataSource {
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
extension CodeViewController: UICollectionViewDelegate {
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
scalingCarousel.didScroll()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>2.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -18,6 +18,7 @@ class StoryboardViewController: UIViewController {
|
||||
// MARK: - IBOutlets
|
||||
@IBOutlet weak var carousel: ScalingCarouselView!
|
||||
@IBOutlet weak var carouselBottomConstraint: NSLayoutConstraint!
|
||||
@IBOutlet weak var output: UILabel!
|
||||
|
||||
private struct Constants {
|
||||
static let carouselHideConstant: CGFloat = -250
|
||||
@@ -59,3 +60,15 @@ extension CarouselDatasource: UICollectionViewDataSource {
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
typealias CarouselDelegate = StoryboardViewController
|
||||
extension StoryboardViewController: UICollectionViewDelegate {
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
carousel.didScroll()
|
||||
|
||||
guard let currentCenterIndex = carousel.currentCenterCellIndex?.row else { return }
|
||||
|
||||
output.text = String(describing: currentCenterIndex)
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 930 KiB |
Binary file not shown.
@@ -8,7 +8,9 @@
|
||||
ScalingCarousel provides a simple carousel-style collection view.
|
||||
It takes care of cell presentation, scaling each cell as the collection view is scrolled.
|
||||
|
||||
It is used in [Bikey](https://itunes.apple.com/ie/app/bikey/id1048962300?mt=8) to present bike station information.
|
||||
It is used in [Bikey](https://itunes.apple.com/ie/app/bikey/id1048962300?mt=8) to present bike station information, as seen below;
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
@@ -16,20 +18,22 @@ ScalingCarousel can be added via both storyboard/xib and code, as described belo
|
||||
|
||||
### Storyboard
|
||||
|
||||
1. Add a UICollectionView to your view, and change the type to ScalingCarouselView
|
||||
* Add a UICollectionView to your view, and change the type to ScalingCarouselView
|
||||
|
||||
2. In the attributes inspector, set the desired carousel inset
|
||||
* In the attributes inspector, set the desired carousel inset
|
||||
|
||||
3. Set your UIViewController as the collection view datasource and implement the standard UICollectionViewDatasource methods in your view controller
|
||||
* Set your UIViewController as the collection view datasource and implement the standard UICollectionViewDatasource methods in your view controller
|
||||
|
||||
4. Create a custom UICollectionViewCell which inherits from ScalingCarouselCell, and set the cell type to your custom cell type in the storyboard
|
||||
* Set your UIViewController as the collection view delegate and implement the UIScrollViewDelegate method scrollViewDidScroll(:). In this method, call the didScroll() method of ScalingCarouselView
|
||||
|
||||
5. Add a view to the cell's content view, and connect this via the Connections Inspector (in Interface builder) to the cell's mainView IBOutlet.
|
||||
* Create a custom UICollectionViewCell which inherits from ScalingCarouselCell, and set the cell type to your custom cell type in the storyboard
|
||||
|
||||
* Add a view to the cell's content view, and connect this via the Connections Inspector (in Interface builder) to the cell's mainView IBOutlet.
|
||||
This property is declared in ScalingCarouselCell. You should add any cell content to this view.
|
||||
|
||||
### Code
|
||||
|
||||
1. Create a custom UICollectionViewCell which inherits from ScalingCarouselCell. Initialize the mainView property, which is declared in ScalingCarouselCell;
|
||||
* Create a custom UICollectionViewCell which inherits from ScalingCarouselCell. Initialize the mainView property, which is declared in ScalingCarouselCell;
|
||||
|
||||
```
|
||||
override init(frame: CGRect) {
|
||||
@@ -41,7 +45,7 @@ override init(frame: CGRect) {
|
||||
}
|
||||
```
|
||||
|
||||
2. Create and add a ScalingCarouselView to your view, and implement the standard UICollectionViewDatasource methods in your view controller;
|
||||
* Create and add a ScalingCarouselView to your view, and implement the standard UICollectionViewDatasource methods in your view controller;
|
||||
|
||||
```
|
||||
// Create our carousel
|
||||
@@ -62,6 +66,8 @@ scalingCarousel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive =
|
||||
scalingCarousel.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true
|
||||
```
|
||||
|
||||
* Set your UIViewController as the collection view delegate and implement the UIScrollViewDelegate method scrollViewDidScroll(:). In this method, call the didScroll() method of ScalingCarouselView
|
||||
|
||||
## Example
|
||||
|
||||
To run the example project, clone the repo, and run `pod install` from the Example directory first.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'ScalingCarousel'
|
||||
s.version = '1.0'
|
||||
s.version = '2.0'
|
||||
s.summary = 'A super simple carousel view with scaling transitions written in Swift'
|
||||
|
||||
s.description = <<-DESC
|
||||
|
||||
@@ -51,22 +51,38 @@ open class ScalingCarouselCell: UICollectionViewCell {
|
||||
/// - parameter carouselInset: The inset of the related SPBCarousel view
|
||||
/// - parameter scaleMinimum: The minimun % a cell should scale to,
|
||||
/// expressed as a value between 0.0 and 1.0
|
||||
open func scale(withCarouselInset carouselInset: CGFloat, scaleMinimum: CGFloat = 0.9) {
|
||||
guard let superview = superview, let mainView = mainView else { return }
|
||||
open func scale(withCarouselInset carouselInset: CGFloat,
|
||||
scaleMinimum: CGFloat = 0.9) {
|
||||
|
||||
// Ensure we have a superView, and mainView
|
||||
guard let superview = superview,
|
||||
let mainView = mainView else { return }
|
||||
|
||||
// Get our absolute origin value
|
||||
let originX = superview.convert(frame, to: nil).origin.x
|
||||
|
||||
// Calculate our actual origin.x value using our inset
|
||||
let originXActual = originX - carouselInset
|
||||
|
||||
let width = frame.size.width
|
||||
|
||||
// Calculate our scale values
|
||||
let scaleCalculator = fabs(width - fabs(originXActual))
|
||||
let percentageScale = (scaleCalculator/width)
|
||||
|
||||
let scaleValue = scaleMinimum + (percentageScale/InternalConstants.scaleDivisor)
|
||||
let alphaValue = InternalConstants.alphaSmallestValue + (percentageScale/InternalConstants.scaleDivisor)
|
||||
let scaleValue = scaleMinimum
|
||||
+ (percentageScale/InternalConstants.scaleDivisor)
|
||||
|
||||
mainView.transform = CGAffineTransform.identity.scaledBy(x: scaleValue, y: scaleValue)
|
||||
let alphaValue = InternalConstants.alphaSmallestValue
|
||||
+ (percentageScale/InternalConstants.scaleDivisor)
|
||||
|
||||
let affineIdentity = CGAffineTransform.identity
|
||||
|
||||
// Scale our mainView and set it's alpha value
|
||||
mainView.transform = affineIdentity.scaledBy(x: scaleValue, y: scaleValue)
|
||||
mainView.alpha = alphaValue
|
||||
|
||||
// ..also..round the corners
|
||||
mainView.layer.cornerRadius = 20
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,14 @@ open class ScalingCarouselLayout: UICollectionViewFlowLayout {
|
||||
}
|
||||
|
||||
override open func prepare() {
|
||||
|
||||
guard let collectionViewSize = collectionView?.frame.size else { return }
|
||||
|
||||
// Set itemSize based on total width and inset
|
||||
itemSize = collectionViewSize
|
||||
itemSize.width = itemSize.width - (inset * 2)
|
||||
|
||||
// Set scrollDirection and paging
|
||||
scrollDirection = .horizontal
|
||||
collectionView?.isPagingEnabled = true
|
||||
|
||||
|
||||
@@ -19,33 +19,80 @@ open class ScalingCarouselView: UICollectionView {
|
||||
|
||||
// MARK: - Properties (Public)
|
||||
|
||||
/// Inset of the main, central cell
|
||||
/// Inset of the main, center cell
|
||||
@IBInspectable public var inset: CGFloat = 0.0 {
|
||||
didSet {
|
||||
collectionViewLayout = ScalingCarouselLayout(withCarouselInset: inset)
|
||||
/*
|
||||
Configure our layout, and add more
|
||||
constraints to our invisible UIScrollView
|
||||
*/
|
||||
configureLayout()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current center cell of the carousel if it can be calculated
|
||||
public var currentCenterCell: UICollectionViewCell? {
|
||||
|
||||
let lowerBound = inset - 20
|
||||
let upperBound = inset + 20
|
||||
|
||||
for cell in visibleCells {
|
||||
|
||||
let cellRect = convert(cell.frame, to: nil)
|
||||
|
||||
if cellRect.origin.x > lowerBound && cellRect.origin.x < upperBound {
|
||||
return cell
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Returns the IndexPath of the current center cell if it can be calculated
|
||||
public var currentCenterCellIndex: IndexPath? {
|
||||
guard let currentCenterCell = self.currentCenterCell else { return nil }
|
||||
|
||||
return indexPath(for: currentCenterCell)
|
||||
}
|
||||
|
||||
/// Override of the collection view content size to add an observer
|
||||
override open var contentSize: CGSize {
|
||||
didSet {
|
||||
invisibleScrollView.contentSize = contentSize
|
||||
|
||||
guard let dataSource = dataSource,
|
||||
let invisibleScrollView = invisibleScrollView else { return }
|
||||
|
||||
let numberSections = dataSource.numberOfSections?(in: self) ?? 1
|
||||
|
||||
// Calculate total number of items in collection view
|
||||
var numberItems = 0
|
||||
|
||||
for i in 0..<numberSections {
|
||||
|
||||
let numberSectionItems = dataSource.collectionView(self, numberOfItemsInSection: i)
|
||||
numberItems += numberSectionItems
|
||||
}
|
||||
|
||||
// Set the invisibleScrollView contentSize width based on number of items
|
||||
let contentWidth = invisibleScrollView.frame.width * CGFloat(numberItems)
|
||||
invisibleScrollView.contentSize = CGSize(width: contentWidth, height: invisibleScrollView.frame.height)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Properties (Private)
|
||||
fileprivate var invisibleScrollView: UIScrollView!
|
||||
fileprivate var invisibleWidthConstraint: NSLayoutConstraint?
|
||||
fileprivate var invisibleLeftConstraint: NSLayoutConstraint?
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override public init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
|
||||
super.init(frame: frame, collectionViewLayout: layout)
|
||||
configure()
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
configure()
|
||||
}
|
||||
|
||||
/// Convenience initializer allowing setting of the carousel inset
|
||||
@@ -61,16 +108,6 @@ open class ScalingCarouselView: UICollectionView {
|
||||
|
||||
// MARK: - Overrides
|
||||
|
||||
override open func updateConstraints() {
|
||||
|
||||
invisibleScrollView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
|
||||
invisibleScrollView.widthAnchor.constraint(equalTo: widthAnchor, constant: -(2 * inset)).isActive = true
|
||||
invisibleScrollView.leftAnchor.constraint(equalTo: leftAnchor, constant: inset).isActive = true
|
||||
invisibleScrollView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
|
||||
super.updateConstraints()
|
||||
}
|
||||
|
||||
override open func scrollRectToVisible(_ rect: CGRect, animated: Bool) {
|
||||
invisibleScrollView.setContentOffset(rect.origin, animated: animated)
|
||||
}
|
||||
@@ -83,23 +120,104 @@ open class ScalingCarouselView: UICollectionView {
|
||||
scrollRectToVisible(rect, animated: animated)
|
||||
}
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
private func configure() {
|
||||
override open func didMoveToSuperview() {
|
||||
super.didMoveToSuperview()
|
||||
|
||||
// Add our invisible scrollview
|
||||
invisibleScrollView = UIScrollView(frame: self.bounds)
|
||||
addInvisibleScrollView(to: superview)
|
||||
}
|
||||
|
||||
// MARK: - Public API
|
||||
|
||||
/*
|
||||
This method should ALWAYS be called from the ScalingCarousel delegate when
|
||||
the UIScrollViewDelegate scrollViewDidScroll(_:) method is called
|
||||
|
||||
e.g In the ScalingCarousel delegate, implement:
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
carousel.didScroll()
|
||||
}
|
||||
*/
|
||||
public func didScroll() {
|
||||
scrollViewDidScroll(self)
|
||||
}
|
||||
}
|
||||
|
||||
private typealias PrivateAPI = ScalingCarouselView
|
||||
fileprivate extension PrivateAPI {
|
||||
|
||||
fileprivate func addInvisibleScrollView(to superview: UIView?) {
|
||||
guard let superview = superview else { return }
|
||||
|
||||
/// Add our 'invisible' scrollview
|
||||
invisibleScrollView = UIScrollView(frame: bounds)
|
||||
invisibleScrollView.translatesAutoresizingMaskIntoConstraints = false
|
||||
invisibleScrollView.isPagingEnabled = true
|
||||
invisibleScrollView.showsHorizontalScrollIndicator = false
|
||||
|
||||
// Turn off interaction on the overlay so touch events fall through to the main scroll view
|
||||
/*
|
||||
Disable user interaction on the 'invisible' scrollview,
|
||||
This means touch events will fall through to the underlying UICollectionView
|
||||
*/
|
||||
invisibleScrollView.isUserInteractionEnabled = false
|
||||
|
||||
/// Set the scroll delegate to be the ScalingCarouselView
|
||||
invisibleScrollView.delegate = self
|
||||
|
||||
// Add the disabled invisible scrollviews pan gesture recognizer to our collection view
|
||||
/*
|
||||
Now add the invisible scrollview's pan
|
||||
gesture recognizer to the ScalingCarouselView
|
||||
*/
|
||||
addGestureRecognizer(invisibleScrollView.panGestureRecognizer)
|
||||
addSubview(invisibleScrollView)
|
||||
|
||||
/*
|
||||
Finally, add the 'invisible' scrollview as a subview
|
||||
of the ScalingCarousel's superview
|
||||
*/
|
||||
superview.addSubview(invisibleScrollView)
|
||||
|
||||
/*
|
||||
Add constraints for height and top, relative to the
|
||||
ScalingCarouselView
|
||||
*/
|
||||
invisibleScrollView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
|
||||
invisibleScrollView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
|
||||
/*
|
||||
Further configure our layout and add more constraints
|
||||
for width and left position
|
||||
*/
|
||||
configureLayout()
|
||||
}
|
||||
|
||||
fileprivate func configureLayout() {
|
||||
|
||||
// Create a ScalingCarouselLayout using our inset
|
||||
collectionViewLayout = ScalingCarouselLayout(
|
||||
withCarouselInset: inset)
|
||||
|
||||
/*
|
||||
Only continue if we have a reference to
|
||||
our 'invisible' UIScrollView
|
||||
*/
|
||||
guard let invisibleScrollView = invisibleScrollView else { return }
|
||||
|
||||
// Remove constraints if they already exist
|
||||
invisibleWidthConstraint?.isActive = false
|
||||
invisibleLeftConstraint?.isActive = false
|
||||
|
||||
/*
|
||||
Add constrants for width and left postion
|
||||
to our 'invisible' UIScrollView
|
||||
*/
|
||||
invisibleWidthConstraint = invisibleScrollView.widthAnchor.constraint(
|
||||
equalTo: widthAnchor, constant: -(2 * inset))
|
||||
invisibleLeftConstraint = invisibleScrollView.leftAnchor.constraint(
|
||||
equalTo: leftAnchor, constant: inset)
|
||||
|
||||
// Activate the constraints
|
||||
invisibleWidthConstraint?.isActive = true
|
||||
invisibleLeftConstraint?.isActive = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,12 +229,13 @@ extension InvisibleScrollDelegate: UIScrollViewDelegate {
|
||||
|
||||
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
|
||||
delegate?.scrollViewDidScroll?(scrollView)
|
||||
|
||||
// Only move the collection view by an amount based on the invisible scrollview
|
||||
/*
|
||||
Move the ScalingCarousel base d on the
|
||||
contentOffset of the 'invisible' UIScrollView
|
||||
*/
|
||||
updateOffSet()
|
||||
|
||||
// Scale Visible cells
|
||||
// Also, this is where we scale our cells
|
||||
for cell in visibleCells {
|
||||
if let infoCardCell = cell as? ScalingCarouselCell {
|
||||
infoCardCell.scale(withCarouselInset: inset)
|
||||
|
||||
Reference in New Issue
Block a user