diff --git a/DZNEmptyDataSet.podspec b/DZNEmptyDataSet.podspec index ed3d2440..693bb5d2 100644 --- a/DZNEmptyDataSet.podspec +++ b/DZNEmptyDataSet.podspec @@ -1,4 +1,4 @@ -@version = "1.7.3" +@version = "2.0.0beta" Pod::Spec.new do |s| s.name = "DZNEmptyDataSet" diff --git a/Examples/Sample/Sample.xcodeproj/project.pbxproj b/Examples/Sample/Sample.xcodeproj/project.pbxproj index 5430995a..ea7138f6 100644 --- a/Examples/Sample/Sample.xcodeproj/project.pbxproj +++ b/Examples/Sample/Sample.xcodeproj/project.pbxproj @@ -38,7 +38,7 @@ /* Begin PBXFileReference section */ F51674491C8447FE0043E515 /* UIScrollView+EmptyDataSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+EmptyDataSet.h"; path = "../Old/UIScrollView+EmptyDataSet.h"; sourceTree = ""; }; F516744A1C8447FE0043E515 /* UIScrollView+EmptyDataSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIScrollView+EmptyDataSet.m"; path = "../Old/UIScrollView+EmptyDataSet.m"; sourceTree = ""; }; - F516744C1C8448060043E515 /* DZNEmptyDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DZNEmptyDataSet.swift; path = ../../Source/DZNEmptyDataSet.swift; sourceTree = ""; }; + F516744C1C8448060043E515 /* DZNEmptyDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DZNEmptyDataSet.swift; path = ../../Source/Swift/DZNEmptyDataSet.swift; sourceTree = ""; }; F51CCC5A1C240C4E008BC8EE /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; F51CCC5D1C240C4E008BC8EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; F51CCC5F1C240C4E008BC8EE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; diff --git a/Source/DZNEmptyDataSet.xcodeproj/project.pbxproj b/Source/DZNEmptyDataSet.xcodeproj/project.pbxproj new file mode 100644 index 00000000..db68e3d3 --- /dev/null +++ b/Source/DZNEmptyDataSet.xcodeproj/project.pbxproj @@ -0,0 +1,406 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + A7209B291D197FFE00E8C362 /* DZNEmptyDataSet.h in Headers */ = {isa = PBXBuildFile; fileRef = A7209B281D197FFE00E8C362 /* DZNEmptyDataSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A7209B301D197FFF00E8C362 /* DZNEmptyDataSet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7209B251D197FFE00E8C362 /* DZNEmptyDataSet.framework */; }; + A7209B351D197FFF00E8C362 /* DZNEmptyDataSetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7209B341D197FFF00E8C362 /* DZNEmptyDataSetTests.swift */; }; + A7209B401D19801100E8C362 /* DZNEmptyDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7209B3F1D19801100E8C362 /* DZNEmptyDataSet.swift */; }; + A77053041D1A854B00853FFF /* TableViewEmptyDataSetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A77053031D1A854B00853FFF /* TableViewEmptyDataSetTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + A7209B311D197FFF00E8C362 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A7209B1C1D197FFE00E8C362 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A7209B241D197FFE00E8C362; + remoteInfo = DZNEmptyDataSet; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + A7209B251D197FFE00E8C362 /* DZNEmptyDataSet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DZNEmptyDataSet.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A7209B281D197FFE00E8C362 /* DZNEmptyDataSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DZNEmptyDataSet.h; sourceTree = ""; }; + A7209B2A1D197FFE00E8C362 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A7209B2F1D197FFF00E8C362 /* DZNEmptyDataSetTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DZNEmptyDataSetTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A7209B341D197FFF00E8C362 /* DZNEmptyDataSetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DZNEmptyDataSetTests.swift; sourceTree = ""; }; + A7209B361D197FFF00E8C362 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A7209B3F1D19801100E8C362 /* DZNEmptyDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DZNEmptyDataSet.swift; path = ../Swift/DZNEmptyDataSet.swift; sourceTree = ""; }; + A77053031D1A854B00853FFF /* TableViewEmptyDataSetTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewEmptyDataSetTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A7209B211D197FFE00E8C362 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A7209B2C1D197FFF00E8C362 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A7209B301D197FFF00E8C362 /* DZNEmptyDataSet.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A7209B1B1D197FFE00E8C362 = { + isa = PBXGroup; + children = ( + A7209B271D197FFE00E8C362 /* DZNEmptyDataSet */, + A7209B331D197FFF00E8C362 /* DZNEmptyDataSetTests */, + A7209B261D197FFE00E8C362 /* Products */, + ); + sourceTree = ""; + }; + A7209B261D197FFE00E8C362 /* Products */ = { + isa = PBXGroup; + children = ( + A7209B251D197FFE00E8C362 /* DZNEmptyDataSet.framework */, + A7209B2F1D197FFF00E8C362 /* DZNEmptyDataSetTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + A7209B271D197FFE00E8C362 /* DZNEmptyDataSet */ = { + isa = PBXGroup; + children = ( + A7209B3F1D19801100E8C362 /* DZNEmptyDataSet.swift */, + A7209B281D197FFE00E8C362 /* DZNEmptyDataSet.h */, + A7209B2A1D197FFE00E8C362 /* Info.plist */, + ); + path = DZNEmptyDataSet; + sourceTree = ""; + }; + A7209B331D197FFF00E8C362 /* DZNEmptyDataSetTests */ = { + isa = PBXGroup; + children = ( + A7209B341D197FFF00E8C362 /* DZNEmptyDataSetTests.swift */, + A7209B361D197FFF00E8C362 /* Info.plist */, + A77053031D1A854B00853FFF /* TableViewEmptyDataSetTests.swift */, + ); + path = DZNEmptyDataSetTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + A7209B221D197FFE00E8C362 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A7209B291D197FFE00E8C362 /* DZNEmptyDataSet.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + A7209B241D197FFE00E8C362 /* DZNEmptyDataSet */ = { + isa = PBXNativeTarget; + buildConfigurationList = A7209B391D197FFF00E8C362 /* Build configuration list for PBXNativeTarget "DZNEmptyDataSet" */; + buildPhases = ( + A7209B201D197FFE00E8C362 /* Sources */, + A7209B211D197FFE00E8C362 /* Frameworks */, + A7209B221D197FFE00E8C362 /* Headers */, + A7209B231D197FFE00E8C362 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DZNEmptyDataSet; + productName = DZNEmptyDataSet; + productReference = A7209B251D197FFE00E8C362 /* DZNEmptyDataSet.framework */; + productType = "com.apple.product-type.framework"; + }; + A7209B2E1D197FFF00E8C362 /* DZNEmptyDataSetTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = A7209B3C1D197FFF00E8C362 /* Build configuration list for PBXNativeTarget "DZNEmptyDataSetTests" */; + buildPhases = ( + A7209B2B1D197FFF00E8C362 /* Sources */, + A7209B2C1D197FFF00E8C362 /* Frameworks */, + A7209B2D1D197FFF00E8C362 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A7209B321D197FFF00E8C362 /* PBXTargetDependency */, + ); + name = DZNEmptyDataSetTests; + productName = DZNEmptyDataSetTests; + productReference = A7209B2F1D197FFF00E8C362 /* DZNEmptyDataSetTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A7209B1C1D197FFE00E8C362 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = KenziTrader; + TargetAttributes = { + A7209B241D197FFE00E8C362 = { + CreatedOnToolsVersion = 7.3.1; + }; + A7209B2E1D197FFF00E8C362 = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = A7209B1F1D197FFE00E8C362 /* Build configuration list for PBXProject "DZNEmptyDataSet" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = A7209B1B1D197FFE00E8C362; + productRefGroup = A7209B261D197FFE00E8C362 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A7209B241D197FFE00E8C362 /* DZNEmptyDataSet */, + A7209B2E1D197FFF00E8C362 /* DZNEmptyDataSetTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A7209B231D197FFE00E8C362 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A7209B2D1D197FFF00E8C362 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A7209B201D197FFE00E8C362 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A7209B401D19801100E8C362 /* DZNEmptyDataSet.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A7209B2B1D197FFF00E8C362 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A77053041D1A854B00853FFF /* TableViewEmptyDataSetTests.swift in Sources */, + A7209B351D197FFF00E8C362 /* DZNEmptyDataSetTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + A7209B321D197FFF00E8C362 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A7209B241D197FFE00E8C362 /* DZNEmptyDataSet */; + targetProxy = A7209B311D197FFF00E8C362 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + A7209B371D197FFF00E8C362 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = 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_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + 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 = ( + "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 = 9.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + A7209B381D197FFF00E8C362 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = 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_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 9.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + A7209B3A1D197FFF00E8C362 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = DZNEmptyDataSet/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.latolato.kenzitrader.DZNEmptyDataSet; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + A7209B3B1D197FFF00E8C362 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = DZNEmptyDataSet/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.latolato.kenzitrader.DZNEmptyDataSet; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + A7209B3D1D197FFF00E8C362 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = DZNEmptyDataSetTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.latolato.kenzitrader.DZNEmptyDataSetTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + A7209B3E1D197FFF00E8C362 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = DZNEmptyDataSetTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.latolato.kenzitrader.DZNEmptyDataSetTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A7209B1F1D197FFE00E8C362 /* Build configuration list for PBXProject "DZNEmptyDataSet" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A7209B371D197FFF00E8C362 /* Debug */, + A7209B381D197FFF00E8C362 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A7209B391D197FFF00E8C362 /* Build configuration list for PBXNativeTarget "DZNEmptyDataSet" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A7209B3A1D197FFF00E8C362 /* Debug */, + A7209B3B1D197FFF00E8C362 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A7209B3C1D197FFF00E8C362 /* Build configuration list for PBXNativeTarget "DZNEmptyDataSetTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A7209B3D1D197FFF00E8C362 /* Debug */, + A7209B3E1D197FFF00E8C362 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A7209B1C1D197FFE00E8C362 /* Project object */; +} diff --git a/Source/DZNEmptyDataSet.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Source/DZNEmptyDataSet.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..8be7b8bc --- /dev/null +++ b/Source/DZNEmptyDataSet.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Source/DZNEmptyDataSet/DZNEmptyDataSet.h b/Source/DZNEmptyDataSet/DZNEmptyDataSet.h new file mode 100644 index 00000000..2c733054 --- /dev/null +++ b/Source/DZNEmptyDataSet/DZNEmptyDataSet.h @@ -0,0 +1,19 @@ +// +// DZNEmptyDataSet.h +// DZNEmptyDataSet +// +// Created by Rene on 21/06/16. +// Copyright © 2016 KenziTrader. All rights reserved. +// + +#import + +//! Project version number for DZNEmptyDataSet. +FOUNDATION_EXPORT double DZNEmptyDataSetVersionNumber; + +//! Project version string for DZNEmptyDataSet. +FOUNDATION_EXPORT const unsigned char DZNEmptyDataSetVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Source/DZNEmptyDataSet/Info.plist b/Source/DZNEmptyDataSet/Info.plist new file mode 100644 index 00000000..d3de8eef --- /dev/null +++ b/Source/DZNEmptyDataSet/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Source/DZNEmptyDataSetTests/DZNEmptyDataSetTests.swift b/Source/DZNEmptyDataSetTests/DZNEmptyDataSetTests.swift new file mode 100644 index 00000000..8e35ba50 --- /dev/null +++ b/Source/DZNEmptyDataSetTests/DZNEmptyDataSetTests.swift @@ -0,0 +1,36 @@ +// +// DZNEmptyDataSetTests.swift +// DZNEmptyDataSetTests +// +// Created by Rene on 21/06/16. +// Copyright © 2016 KenziTrader. All rights reserved. +// + +import XCTest +@testable import DZNEmptyDataSet + +class DZNEmptyDataSetTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measureBlock { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Source/DZNEmptyDataSetTests/Info.plist b/Source/DZNEmptyDataSetTests/Info.plist new file mode 100644 index 00000000..ba72822e --- /dev/null +++ b/Source/DZNEmptyDataSetTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Source/DZNEmptyDataSetTests/TableViewEmptyDataSetTests.swift b/Source/DZNEmptyDataSetTests/TableViewEmptyDataSetTests.swift new file mode 100644 index 00000000..d6deb5a4 --- /dev/null +++ b/Source/DZNEmptyDataSetTests/TableViewEmptyDataSetTests.swift @@ -0,0 +1,159 @@ +// +// TableViewEmptyDataSetTests.swift +// DZNEmptyDataSet +// +// Created by Rene on 22/06/16. +// Copyright © 2016 KenziTrader. All rights reserved. +// + +import XCTest +@testable import DZNEmptyDataSet + +class TableViewEmptyDataSetTests: XCTestCase { + // MARK: Subject under test + + // MARK: Test lifecycle + + override func setUp() + { + super.setUp() + } + + override func tearDown() + { + super.tearDown() + } + + // MARK: Test setup + + // MARK: Test doubles + + class TableViewWithEmptyDataSet: UITableView, UITableViewDataSource, DZNEmptyDataSetSource + { + var items = [String]() + + override init(frame: CGRect, style: UITableViewStyle) { + super.init(frame: frame, style: style) + dataSource = self + emptyDataSetSource = self + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + print("\(self.dynamicType)\(#function) count=\(items.count)") + return items.count + } + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + + let cell = UITableViewCell() + + return cell + } + + func titleForEmptyDataSet(scrollView: UIScrollView) -> NSAttributedString? { + let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(27), NSForegroundColorAttributeName: UIColor.lightGrayColor()] + + return NSAttributedString.init(string: "No items found", attributes: attributes) + } + + func refresh() { + items.append("Hello") + } + + func deleteAll() { + items = [] + } + + } + + class CollectionViewWithEmptyDataSet: UICollectionView, UICollectionViewDataSource, DZNEmptyDataSetSource + { + var items = [String]() + + override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { + super.init(frame: frame, collectionViewLayout: layout) + dataSource = self + emptyDataSetSource = self + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + print("\(self.dynamicType)\(#function) count=\(items.count)") + return items.count + } + + func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell + { + let cell = UICollectionViewCell() + + return cell + } + + func titleForEmptyDataSet(scrollView: UIScrollView) -> NSAttributedString? { + let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(27), NSForegroundColorAttributeName: UIColor.lightGrayColor()] + + return NSAttributedString.init(string: "No items found", attributes: attributes) + } + + func refresh() { + items.append("Hello") + } + + func deleteAll() { + items = [] + } + + } + + // MARK: Tests + + func testTableViewWithEmptyDataSetHasPlaceholderVisible() + { + // Given + let tableView = TableViewWithEmptyDataSet() + tableView.deleteAll() + + // When + tableView.reloadEmptyDataSet() + + // Then + XCTAssert(tableView.isEmptyDataSetVisible, "With an empty table the EmptyDataSetView should be visible") + } + + func testTableViewWithDataSetHasNoPlaceholderVisible() + { + // Given + let tableView = TableViewWithEmptyDataSet() + + // When + tableView.beginUpdates() + tableView.refresh() + tableView.endUpdates() + + // Then + XCTAssert(!tableView.isEmptyDataSetVisible, "With data in the table the EmptyDataSetView should not be visible") + } + + func testCollectionViewWithEmptyDataSetHasPlaceholderVisible() + { + // Given + let layout = UICollectionViewFlowLayout() + let frame = CGRectZero + let collectionView = CollectionViewWithEmptyDataSet(frame: frame, collectionViewLayout: layout) + collectionView.deleteAll() + + // When + collectionView.reloadEmptyDataSet() + + // Then + XCTAssert(collectionView.isEmptyDataSetVisible, "With an empty table the EmptyDataSetView should be visible") + } + +} diff --git a/Source/Swift/DZNEmptyDataSet.swift b/Source/Swift/DZNEmptyDataSet.swift index 2a7dfbf8..c526a0b5 100644 --- a/Source/Swift/DZNEmptyDataSet.swift +++ b/Source/Swift/DZNEmptyDataSet.swift @@ -8,61 +8,228 @@ import UIKit -// TODO: Add documentation once completed +/// The protocol that acts as the data source of the empty datasets. +/// +/// The data source must adopt the DZNEmptyDataSetSource protocol. The data source is not retained. All data source methods are optional. @objc public protocol DZNEmptyDataSetSource : NSObjectProtocol { /// optional func sectionsToIgnore(scrollView: UIScrollView) -> NSIndexSet? + /** + Asks the data source for the title of the dataset. + The dataset uses a fixed font style by default, if no attributes are set. If you want a different font style, return a attributed string. + + - Parameter scrollView: A scrollView subclass informing the data source. + - Returns: An attributed string for the dataset title, combining font, text color, text pararaph style, etc. + */ optional func titleForEmptyDataSet(scrollView: UIScrollView) -> NSAttributedString? + /** + Asks the data source for the description of the dataset. + The dataset uses a fixed font style by default, if no attributes are set. If you want a different font style, return a attributed string. + + - Parameter scrollView: A scrollView subclass informing the data source. + - Returns: An attributed string for the dataset description text, combining font, text color, text pararaph style, etc. + */ optional func descriptionForEmptyDataSet(scrollView: UIScrollView) -> NSAttributedString? + /** + Asks the data source for the image of the dataset. + + - Parameter scrollView: A scrollView subclass informing the data source. + - Returns: An image for the dataset. + */ optional func imageForEmptyDataSet(scrollView: UIScrollView) -> UIImage? + /** + Asks the data source for a tint color of the image dataset. Default is nil. + + - Parameter scrollView: A scrollView subclass object informing the data source. + - Returns: A color to tint the image of the dataset. + */ optional func imageTintColorForEmptyDataSet(scrollView: UIScrollView) -> UIImage? + /** + * Asks the data source for the image animation of the dataset. + * + * - Parameter scrollView: A scrollView subclass object informing the delegate. + * + * - Returns: image animation + */ + optional func imageAnimationForEmptyDataSet(scrollView: UIScrollView) -> CAAnimation + + /** + Asks the data source for the title to be used for the specified button state. + The dataset uses a fixed font style by default, if no attributes are set. If you want a different font style, return a attributed string. + + - Parameter scrollView: A scrollView subclass object informing the data source. + - Parameter state: The state that uses the specified title. The possible values are described in UIControlState. + - Returns: An attributed string for the dataset button title, combining font, text color, text pararaph style, etc. + */ optional func buttonTitleForEmptyDataSet(scrollView: UIScrollView, state: UIControlState) -> NSAttributedString? + /** + Asks the data source for the image to be used for the specified button state. + This method will override buttonTitleForEmptyDataSet:forState: and present the image only without any text. + + - Parameter scrollView: A scrollView subclass object informing the data source. + - Parameter state: The state that uses the specified title. The possible values are described in UIControlState. + - Returns: An image for the dataset button imageview. + */ optional func buttonImageForEmptyDataSet(scrollView: UIScrollView, state: UIControlState) -> UIImage? + /** + Asks the data source for a background image to be used for the specified button state. + There is no default style for this call. + + - Parameter scrollView: A scrollView subclass informing the data source. + - Parameter state: The state that uses the specified image. The values are described in UIControlState. + - Returns: An attributed string for the dataset button title, combining font, text color, text pararaph style, etc. + */ optional func buttonBackgroundImageForEmptyDataSet(scrollView: UIScrollView, state: UIControlState) -> UIImage? + /** + Asks the data source for the background color of the dataset. Default is clear color. + + - Parameter scrollView: A scrollView subclass object informing the data source. + - Returns: A color to be applied to the dataset background view. + */ optional func backgroundColorForEmptyDataSet(scrollView: UIScrollView) -> UIColor? + /** + Asks the data source for a custom view to be displayed instead of the default views such as labels, imageview and button. Default is nil. + Use this method to show an activity view indicator for loading feedback, or for complete custom empty data set. + Returning a custom view will ignore -offsetForEmptyDataSet and -spaceHeightForEmptyDataSet configurations. + + - Parameter scrollView: A scrollView subclass object informing the delegate. + - Returns: The custom view. + */ optional func customViewForEmptyDataSet(scrollView: UIScrollView) -> UIImage? + /** + Asks the data source for a offset for vertical and horizontal alignment of the content. Default is CGPointZero. + + - Parameter scrollView: A scrollView subclass object informing the delegate. + - Returns: The offset for vertical and horizontal alignment. + */ optional func verticalOffsetForEmptyDataSet(scrollView: UIScrollView) -> CGFloat + /** + Asks the data source for a vertical space between elements. Default is 11 pts. + + - Parameter scrollView: A scrollView subclass object informing the delegate. + - Returns: The space height between elements. + */ optional func spaceHeightForEmptyDataSet(scrollView: UIScrollView) -> CGFloat } // TODO: Add documentation once completed +/** + The protocol that acts as the delegate of the empty datasets. + The delegate can adopt the DZNEmptyDataSetDelegate protocol. The delegate is not retained. All delegate methods are optional. + + All delegate methods are optional. Use this delegate for receiving action callbacks. + */ @objc public protocol DZNEmptyDataSetDelegate : NSObjectProtocol { + /** + Asks the delegate to know if the empty dataset should be rendered and displayed. Default is true. + + - Parameter scrollView: A scrollView subclass object informing the delegate. + - Returns: true if the empty dataset should show. + */ optional func emptyDataSetShouldDisplay(scrollView: UIScrollView) -> Bool + /** + Asks the delegate for touch permission. Default is true. + + - Parameter scrollView: A scrollView subclass object informing the delegate. + - Returns: true if the empty dataset receives touch gestures. + */ optional func emptyDataSetShouldAllowTouch(scrollView: UIScrollView) -> Bool + /** + Asks the delegate for scroll permission. Default is false. + + - Parameter scrollView: A scrollView subclass object informing the delegate. + - Returns: true if the empty dataset is allowed to be scrollable. + */ optional func emptyDataSetShouldAllowScroll(scrollView: UIScrollView) -> Bool + /** + Asks the delegate to know if the empty dataset should fade in when displayed. Default is true. + + - Parameter scrollView: A scrollView subclass object informing the delegate. + - Returns: true if the empty dataset should fade in. + */ optional func emptyDataSetShouldFadeIn(scrollView: UIScrollView) -> Bool + /** + Asks the delegate for image view animation permission. Default is false. + Make sure to return a valid CAAnimation object from imageAnimationForEmptyDataSet: + + - Parameter scrollView: A scrollView subclass object informing the delegate. + - Returns: true if the empty dataset is allowed to animate + */ optional func emptyDataSetShouldAnimateImageView(scrollView: UIScrollView) -> Bool + /** + Tells the delegate that the empty dataset view was tapped. + Use this method either to resignFirstResponder of a textfield or searchBar. + + - Parameter scrollView: A scrollView subclass informing the delegate. + - Parameter view: the view tapped by the user + */ optional func emptyDataSet(scrollView: UIScrollView, didTapView: UIView) + /** + Tells the delegate that the action button was tapped. + + - Parameter scrollView: A scrollView subclass informing the delegate. + - Parameter button: the button tapped by the user + */ optional func emptyDataSet(scrollView: UIScrollView, didTapButton: UIButton) + /** + Tells the delegate that the empty data set will appear. + + - Parameter scrollView: A scrollView subclass informing the delegate. + */ optional func emptyDataSetWillAppear(scrollView: UIScrollView) + /** + Tells the delegate that the empty data set did appear. + + - Parameter scrollView: A scrollView subclass informing the delegate. + */ optional func emptyDataSetDidAppear(scrollView: UIScrollView) + /** + Tells the delegate that the empty data set will disappear. + + - Parameter scrollView: A scrollView subclass informing the delegate. + */ optional func emptyDataSetWillDisappear(scrollView: UIScrollView) + /** + Tells the delegate that the empty data set did disappear. + + - Parameter scrollView: A scrollView subclass informing the delegate. + */ optional func emptyDataSetDidDisappear(scrollView: UIScrollView) } +// MARK: - EmptyDataSet protocol + + +/// protocol EmptyDataSet +/// Instances of conforming UIView subclasses can show empty datasets whenever the view has no content to display. +protocol EmptyDataSet { + func doSwizzle() -> Bool + func isEmpty() -> Bool +} + // MARK: - UIScrollView extension extension UIScrollView { @@ -91,9 +258,14 @@ extension UIScrollView { } } - // TODO: Not implemented yet + /// returns true if the Empty Data Set View is visible, + /// false otherwise var isEmptyDataSetVisible: Bool { - return false + if let view = emptyDataSetView { + return !view.hidden + } else { + return false + } } @@ -118,9 +290,9 @@ extension UIScrollView { view = DZNEmptyDataSetView(frame: self.bounds) view?.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] view?.backgroundColor = .clearColor() - view?.hidden = false + view?.hidden = true - let tapGesture = UITapGestureRecognizer.init(target: self, action: Selector("didTapView:")) + let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(didTapView(_:))) view?.addGestureRecognizer(tapGesture) self.emptyDataSetView = view @@ -145,9 +317,6 @@ extension UIScrollView { public func reloadEmptyDataSet() { - // Calls the original implementation - self.reloadEmptyDataSet() - self.invalidateLayout() guard self.canDisplay && self.shouldDisplay else { return } @@ -162,7 +331,7 @@ extension UIScrollView { imageView.image = image; view.contentView.addSubview(imageView) - counter++ + counter += 1 } // Configure title label @@ -170,7 +339,7 @@ extension UIScrollView { label.attributedText = attributedText; view.contentView.addSubview(label) - counter++ + counter += 1 } // Configure detail label @@ -178,7 +347,7 @@ extension UIScrollView { label.attributedText = attributedText; view.contentView.addSubview(label) - counter++ + counter += 1 } // Configure button @@ -186,16 +355,16 @@ extension UIScrollView { button.setAttributedTitle(attributedText, forState: .Normal) view.contentView.addSubview(button) - button.addTarget(self, action: Selector("didTapView:"), forControlEvents: .TouchUpInside) + button.addTarget(self, action: #selector(didTapView(_:)), forControlEvents: .TouchUpInside) - counter++ + counter += 1 } guard counter > 0 else { return } willAppear() - // Configure the contnet view + // Configure the content view view.hidden = false view.clipsToBounds = true view.fadeInOnDisplay = self.shouldFadeIn @@ -215,149 +384,85 @@ extension UIScrollView { didAppear() } - // TODO: Add tests - private var itemsCount: Int { - - var items = 0 - - guard self.respondsToSelector(Selector("dataSource")) else { return items } - - if let tableView = self as? UITableView { - guard let sections = tableView.dataSource?.numberOfSectionsInTableView?(tableView) else { return items } - - for i in 0.. NSAttributedString? { - if let datasource = emptyDataSetSource, callback = datasource.buttonTitleForEmptyDataSet:state: where datasource.respondsToSelector(Selector("buttonTitleForEmptyDataSet:state:")) { - return callback(self, state: state) - } - return nil + return emptyDataSetSource?.buttonTitleForEmptyDataSet?(self, state: state) } private var verticalOffset: CGFloat { - if let datasource = emptyDataSetSource, callback = datasource.verticalOffsetForEmptyDataSet where datasource.respondsToSelector(Selector("verticalOffsetForEmptyDataSet:")) { - return callback(self) - } - return 0 + return emptyDataSetSource?.verticalOffsetForEmptyDataSet?(self) ?? 0 } private func backgroundColor() -> UIColor { - if let color = (emptyDataSetSource?.backgroundColorForEmptyDataSet?(self)) { - return color - } - return .clearColor() + return emptyDataSetSource?.backgroundColorForEmptyDataSet?(self) ?? .clearColor() } private var canDisplay: Bool { - return self.itemsCount > 0 ? false : true + if let selfAsEmptyDataSet = self as? EmptyDataSet { + return selfAsEmptyDataSet.isEmpty() + } else { + print("\(self.dynamicType) should conform to protocol EmptyDataset") + return false + } } private var shouldDisplay: Bool { - if let delegate = emptyDataSetDelegate, callback = delegate.emptyDataSetShouldDisplay where delegate.respondsToSelector(Selector("emptyDataSetShouldDisplay:")) { - return callback(self) - } - return true + return emptyDataSetDelegate?.emptyDataSetShouldDisplay?(self) ?? true } private var shouldFadeIn: Bool { - if let delegate = emptyDataSetDelegate, callback = delegate.emptyDataSetShouldFadeIn where delegate.respondsToSelector(Selector("emptyDataSetShouldFadeIn:")) { - return callback(self) - } - return true + return emptyDataSetDelegate?.emptyDataSetShouldFadeIn?(self) ?? true } private var shouldScroll: Bool { - if let delegate = emptyDataSetDelegate, callback = delegate.emptyDataSetShouldAllowScroll where delegate.respondsToSelector(Selector("emptyDataSetShouldAllowScroll:")) { - return callback(self) - } - return true + return emptyDataSetDelegate?.emptyDataSetShouldAllowScroll?(self) ?? true } private var shouldTouch: Bool { - if let delegate = emptyDataSetDelegate, callback = delegate.emptyDataSetShouldAllowTouch where delegate.respondsToSelector(Selector("emptyDataSetShouldAllowTouch:")) { - return callback(self) - } - return true + return emptyDataSetDelegate?.emptyDataSetShouldAllowTouch?(self) ?? true } func didTapView(sender: AnyObject?) { - if let delegate = emptyDataSetDelegate where delegate.respondsToSelector(Selector("emptyDataSet:didTapView:")) { - - if let view = sender where sender is UIView { - delegate.emptyDataSet?(self, didTapView: view as! UIView) - } - else if let view = sender?.view where sender is UIGestureRecognizer { - delegate.emptyDataSet?(self, didTapView: view!) - } + if let view = sender where sender is UIView { + emptyDataSetDelegate?.emptyDataSet?(self, didTapView: view as! UIView) + } + else if let view = sender?.view where sender is UIGestureRecognizer { + emptyDataSetDelegate?.emptyDataSet?(self, didTapView: view) } } func willAppear() { - if let delegate = emptyDataSetDelegate where delegate.respondsToSelector(Selector("emptyDataSetWillAppear:")) { - delegate.emptyDataSetWillAppear?(self) - } + emptyDataSetDelegate?.emptyDataSetWillAppear?(self) } func didAppear() { - if let delegate = emptyDataSetDelegate where delegate.respondsToSelector(Selector("emptyDataSetDidAppear:")) { - delegate.emptyDataSetDidAppear?(self) - } + emptyDataSetDelegate?.emptyDataSetDidAppear?(self) } func willDisappear() { - if let delegate = emptyDataSetDelegate where delegate.respondsToSelector(Selector("emptyDataSetWillDisappear:")) { - delegate.emptyDataSetWillDisappear?(self) - } + emptyDataSetDelegate?.emptyDataSetWillDisappear?(self) } func didDisappear() { - if let delegate = emptyDataSetDelegate where delegate.respondsToSelector(Selector("emptyDataSetDidDisappear:")) { - delegate.emptyDataSetDidDisappear?(self) - } + emptyDataSetDelegate?.emptyDataSetDidDisappear?(self) } private func invalidateLayout() { @@ -366,9 +471,12 @@ extension UIScrollView { willDisappear() + // hides the empty data set view + emptyDataSetView?.hidden = true + // Cleans up the empty data set view - self.emptyDataSetView?.removeFromSuperview() - self.emptyDataSetView = nil +// self.emptyDataSetView?.removeFromSuperview() +// self.emptyDataSetView = nil self.scrollEnabled = true @@ -381,12 +489,11 @@ extension UIScrollView { private func swizzleIfNeeded() { if !didSwizzle { - let newSelector = Selector("reloadEmptyDataSet") - - didSwizzle = swizzle(Selector("reloadData"), swizzledSelector: newSelector) - - // TODO: Swizzling works, but whenever we swizzle this other method, it breaks. - //didSwizzle = swizzle(Selector("endUpdates"), swizzledSelector: newSelector) + if let selfAsEmptyDataSet = self as? EmptyDataSet { + didSwizzle = selfAsEmptyDataSet.doSwizzle() + } else { + print("\(self.dynamicType) should conform to protocol EmptyDataset") + } } } @@ -411,6 +518,107 @@ extension UIScrollView { } } +// MARK: - +extension UITableView: EmptyDataSet { + + func doSwizzle() -> Bool { + + var didSwizzle = false + + let newReloadDataSelector = #selector(reloadDataEmptyDataSet) + let originalReloadDataSelector = #selector(UITableView.reloadData) + didSwizzle = swizzle(originalReloadDataSelector, swizzledSelector: newReloadDataSelector) + + let newEndUpdatesSelector = #selector(endUpdatesEmptyDataSet) + let originalEndUpdatesSelector = #selector(UITableView.endUpdates) + didSwizzle = didSwizzle && + swizzle(originalEndUpdatesSelector, swizzledSelector: newEndUpdatesSelector) + + return didSwizzle + } + + func isEmpty() -> Bool { + var items = 0 + let sections = dataSource?.numberOfSectionsInTableView?(self) ?? 1 + + for i in 0.. Bool { + + var didSwizzle = false + + let newReloadDataSelector = #selector(reloadDataEmptyDataSet) + let originalReloadDataSelector = #selector(UICollectionView.reloadData) + didSwizzle = swizzle(originalReloadDataSelector, swizzledSelector: newReloadDataSelector) + + let newEndUpdatesSelector = #selector(performBatchUpdatesEmptyDataSet) + let originalEndUpdatesSelector = #selector(UICollectionView.performBatchUpdates(_:completion:)) + didSwizzle = didSwizzle && + swizzle(originalEndUpdatesSelector, swizzledSelector: newEndUpdatesSelector) + + return didSwizzle + } + + func isEmpty() -> Bool { + var items = 0 + let sections = dataSource?.numberOfSectionsInCollectionView?(self) ?? 1 + + for i in 0.. Void)?, completion: ((Bool) -> Void)?) + { + print("\(self.dynamicType).\(#function)") + + // Calls the original implementation + self.performBatchUpdatesEmptyDataSet(updates, completion: completion) + + reloadEmptyDataSet() + } +} // MARK: - DZNEmptyDataSetView private class DZNEmptyDataSetView: UIView, UIGestureRecognizerDelegate {