├── .gitignore ├── Assets ├── demo.gif └── interface-builder.png ├── HCSStarRatingView.podspec ├── HCSStarRatingView.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ └── HCSStarRatingView.xcscheme ├── HCSStarRatingView ├── HCSStarRatingView.h ├── HCSStarRatingView.m └── Info.plist ├── LICENSE ├── Package.swift ├── README.md └── Sample ├── HCSStarRatingViewSample.xcodeproj └── project.pbxproj └── HCSStarRatingViewSample ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj ├── LaunchScreen.xib └── Main.storyboard ├── Images.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── heart-empty.imageset │ ├── Contents.json │ ├── heart-empty.png │ ├── heart-empty@2x.png │ └── heart-empty@3x.png ├── heart-full.imageset │ ├── Contents.json │ ├── heart-full.png │ ├── heart-full@2x.png │ └── heart-full@3x.png └── heart-half.imageset │ ├── Contents.json │ ├── heart-half.png │ ├── heart-half@2x.png │ └── heart-half@3x.png ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | Icon 6 | 7 | # Thumbnails 8 | ._* 9 | 10 | # Files that might appear on external disk 11 | .Spotlight-V100 12 | .Trashes 13 | 14 | # Xcode 15 | .DS_Store 16 | */build/* 17 | *.pbxuser 18 | !default.pbxuser 19 | *.mode1v3 20 | !default.mode1v3 21 | *.mode2v3 22 | !default.mode2v3 23 | *.perspectivev3 24 | !default.perspectivev3 25 | *.xcworkspace 26 | !default.xcworkspace 27 | xcuserdata 28 | profile 29 | *.moved-aside 30 | DerivedData 31 | .idea/ 32 | *.hmap 33 | -------------------------------------------------------------------------------- /Assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Assets/demo.gif -------------------------------------------------------------------------------- /Assets/interface-builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Assets/interface-builder.png -------------------------------------------------------------------------------- /HCSStarRatingView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "HCSStarRatingView" 3 | s.version = "1.5" 4 | s.summary = "Simple star rating view for iOS written in Objective-C" 5 | s.description = "A UIControl subclass to easily provide users with a basic star-rating interface. It's fully accessible, supports all device resolutions and requires no images do render the stars, thanks to PaintCode. You can, however, provide your own custom images if you like." 6 | s.homepage = "https://github.com/hsousa/HCSStarRatingView" 7 | s.license = { :type => "MIT", :file => "LICENSE" } 8 | s.author = { "Hugo" => "hsousa@me.com" } 9 | s.social_media_url = "http://twitter.com/hsousa" 10 | s.platform = :ios 11 | s.ios.deployment_target = '7.0' 12 | s.source = { :git => "https://github.com/hsousa/HCSStarRatingView.git", :tag => "1.5" } 13 | s.source_files = "HCSStarRatingView/*.{h,m}" 14 | s.requires_arc = true 15 | end 16 | -------------------------------------------------------------------------------- /HCSStarRatingView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B6AF40BC1B4FE6D50060F1B8 /* HCSStarRatingView.h in Headers */ = {isa = PBXBuildFile; fileRef = B6AF40BA1B4FE6D50060F1B8 /* HCSStarRatingView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | B6AF40BD1B4FE6D50060F1B8 /* HCSStarRatingView.m in Sources */ = {isa = PBXBuildFile; fileRef = B6AF40BB1B4FE6D50060F1B8 /* HCSStarRatingView.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | B6AF40891B4FE3260060F1B8 /* HCSStarRatingView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HCSStarRatingView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 16 | B6AF408D1B4FE3260060F1B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 17 | B6AF40BA1B4FE6D50060F1B8 /* HCSStarRatingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HCSStarRatingView.h; sourceTree = ""; }; 18 | B6AF40BB1B4FE6D50060F1B8 /* HCSStarRatingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HCSStarRatingView.m; sourceTree = ""; }; 19 | /* End PBXFileReference section */ 20 | 21 | /* Begin PBXFrameworksBuildPhase section */ 22 | B6AF40851B4FE3260060F1B8 /* Frameworks */ = { 23 | isa = PBXFrameworksBuildPhase; 24 | buildActionMask = 2147483647; 25 | files = ( 26 | ); 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXFrameworksBuildPhase section */ 30 | 31 | /* Begin PBXGroup section */ 32 | B6AF407F1B4FE3260060F1B8 = { 33 | isa = PBXGroup; 34 | children = ( 35 | B6AF408B1B4FE3260060F1B8 /* HCSStarRatingView */, 36 | B6AF408A1B4FE3260060F1B8 /* Products */, 37 | ); 38 | sourceTree = ""; 39 | }; 40 | B6AF408A1B4FE3260060F1B8 /* Products */ = { 41 | isa = PBXGroup; 42 | children = ( 43 | B6AF40891B4FE3260060F1B8 /* HCSStarRatingView.framework */, 44 | ); 45 | name = Products; 46 | sourceTree = ""; 47 | }; 48 | B6AF408B1B4FE3260060F1B8 /* HCSStarRatingView */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | B6AF40BA1B4FE6D50060F1B8 /* HCSStarRatingView.h */, 52 | B6AF40BB1B4FE6D50060F1B8 /* HCSStarRatingView.m */, 53 | B6AF408C1B4FE3260060F1B8 /* Supporting Files */, 54 | ); 55 | path = HCSStarRatingView; 56 | sourceTree = ""; 57 | }; 58 | B6AF408C1B4FE3260060F1B8 /* Supporting Files */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | B6AF408D1B4FE3260060F1B8 /* Info.plist */, 62 | ); 63 | name = "Supporting Files"; 64 | sourceTree = ""; 65 | }; 66 | /* End PBXGroup section */ 67 | 68 | /* Begin PBXHeadersBuildPhase section */ 69 | B6AF40861B4FE3260060F1B8 /* Headers */ = { 70 | isa = PBXHeadersBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | B6AF40BC1B4FE6D50060F1B8 /* HCSStarRatingView.h in Headers */, 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | /* End PBXHeadersBuildPhase section */ 78 | 79 | /* Begin PBXNativeTarget section */ 80 | B6AF40881B4FE3260060F1B8 /* HCSStarRatingView */ = { 81 | isa = PBXNativeTarget; 82 | buildConfigurationList = B6AF409F1B4FE3260060F1B8 /* Build configuration list for PBXNativeTarget "HCSStarRatingView" */; 83 | buildPhases = ( 84 | B6AF40841B4FE3260060F1B8 /* Sources */, 85 | B6AF40851B4FE3260060F1B8 /* Frameworks */, 86 | B6AF40861B4FE3260060F1B8 /* Headers */, 87 | B6AF40871B4FE3260060F1B8 /* Resources */, 88 | ); 89 | buildRules = ( 90 | ); 91 | dependencies = ( 92 | ); 93 | name = HCSStarRatingView; 94 | productName = HCSStarRatingView; 95 | productReference = B6AF40891B4FE3260060F1B8 /* HCSStarRatingView.framework */; 96 | productType = "com.apple.product-type.framework"; 97 | }; 98 | /* End PBXNativeTarget section */ 99 | 100 | /* Begin PBXProject section */ 101 | B6AF40801B4FE3260060F1B8 /* Project object */ = { 102 | isa = PBXProject; 103 | attributes = { 104 | LastUpgradeCheck = 0640; 105 | ORGANIZATIONNAME = hugocampossousa; 106 | TargetAttributes = { 107 | B6AF40881B4FE3260060F1B8 = { 108 | CreatedOnToolsVersion = 6.4; 109 | }; 110 | }; 111 | }; 112 | buildConfigurationList = B6AF40831B4FE3260060F1B8 /* Build configuration list for PBXProject "HCSStarRatingView" */; 113 | compatibilityVersion = "Xcode 3.2"; 114 | developmentRegion = English; 115 | hasScannedForEncodings = 0; 116 | knownRegions = ( 117 | en, 118 | ); 119 | mainGroup = B6AF407F1B4FE3260060F1B8; 120 | productRefGroup = B6AF408A1B4FE3260060F1B8 /* Products */; 121 | projectDirPath = ""; 122 | projectRoot = ""; 123 | targets = ( 124 | B6AF40881B4FE3260060F1B8 /* HCSStarRatingView */, 125 | ); 126 | }; 127 | /* End PBXProject section */ 128 | 129 | /* Begin PBXResourcesBuildPhase section */ 130 | B6AF40871B4FE3260060F1B8 /* Resources */ = { 131 | isa = PBXResourcesBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXResourcesBuildPhase section */ 138 | 139 | /* Begin PBXSourcesBuildPhase section */ 140 | B6AF40841B4FE3260060F1B8 /* Sources */ = { 141 | isa = PBXSourcesBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | B6AF40BD1B4FE6D50060F1B8 /* HCSStarRatingView.m in Sources */, 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | /* End PBXSourcesBuildPhase section */ 149 | 150 | /* Begin XCBuildConfiguration section */ 151 | B6AF409D1B4FE3260060F1B8 /* Debug */ = { 152 | isa = XCBuildConfiguration; 153 | buildSettings = { 154 | ALWAYS_SEARCH_USER_PATHS = NO; 155 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 156 | CLANG_CXX_LIBRARY = "libc++"; 157 | CLANG_ENABLE_MODULES = YES; 158 | CLANG_ENABLE_OBJC_ARC = YES; 159 | CLANG_WARN_BOOL_CONVERSION = YES; 160 | CLANG_WARN_CONSTANT_CONVERSION = YES; 161 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 162 | CLANG_WARN_EMPTY_BODY = YES; 163 | CLANG_WARN_ENUM_CONVERSION = YES; 164 | CLANG_WARN_INT_CONVERSION = YES; 165 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 166 | CLANG_WARN_UNREACHABLE_CODE = YES; 167 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 168 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 169 | COPY_PHASE_STRIP = NO; 170 | CURRENT_PROJECT_VERSION = 1; 171 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 172 | ENABLE_STRICT_OBJC_MSGSEND = YES; 173 | GCC_C_LANGUAGE_STANDARD = gnu99; 174 | GCC_DYNAMIC_NO_PIC = NO; 175 | GCC_NO_COMMON_BLOCKS = YES; 176 | GCC_OPTIMIZATION_LEVEL = 0; 177 | GCC_PREPROCESSOR_DEFINITIONS = ( 178 | "DEBUG=1", 179 | "$(inherited)", 180 | ); 181 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 182 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 183 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 184 | GCC_WARN_UNDECLARED_SELECTOR = YES; 185 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 186 | GCC_WARN_UNUSED_FUNCTION = YES; 187 | GCC_WARN_UNUSED_VARIABLE = YES; 188 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 189 | MTL_ENABLE_DEBUG_INFO = YES; 190 | ONLY_ACTIVE_ARCH = YES; 191 | SDKROOT = iphoneos; 192 | TARGETED_DEVICE_FAMILY = "1,2"; 193 | VERSIONING_SYSTEM = "apple-generic"; 194 | VERSION_INFO_PREFIX = ""; 195 | }; 196 | name = Debug; 197 | }; 198 | B6AF409E1B4FE3260060F1B8 /* Release */ = { 199 | isa = XCBuildConfiguration; 200 | buildSettings = { 201 | ALWAYS_SEARCH_USER_PATHS = NO; 202 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 203 | CLANG_CXX_LIBRARY = "libc++"; 204 | CLANG_ENABLE_MODULES = YES; 205 | CLANG_ENABLE_OBJC_ARC = YES; 206 | CLANG_WARN_BOOL_CONVERSION = YES; 207 | CLANG_WARN_CONSTANT_CONVERSION = YES; 208 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 209 | CLANG_WARN_EMPTY_BODY = YES; 210 | CLANG_WARN_ENUM_CONVERSION = YES; 211 | CLANG_WARN_INT_CONVERSION = YES; 212 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 213 | CLANG_WARN_UNREACHABLE_CODE = YES; 214 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 215 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 216 | COPY_PHASE_STRIP = NO; 217 | CURRENT_PROJECT_VERSION = 1; 218 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 219 | ENABLE_NS_ASSERTIONS = NO; 220 | ENABLE_STRICT_OBJC_MSGSEND = YES; 221 | GCC_C_LANGUAGE_STANDARD = gnu99; 222 | GCC_NO_COMMON_BLOCKS = YES; 223 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 224 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 225 | GCC_WARN_UNDECLARED_SELECTOR = YES; 226 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 227 | GCC_WARN_UNUSED_FUNCTION = YES; 228 | GCC_WARN_UNUSED_VARIABLE = YES; 229 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 230 | MTL_ENABLE_DEBUG_INFO = NO; 231 | SDKROOT = iphoneos; 232 | TARGETED_DEVICE_FAMILY = "1,2"; 233 | VALIDATE_PRODUCT = YES; 234 | VERSIONING_SYSTEM = "apple-generic"; 235 | VERSION_INFO_PREFIX = ""; 236 | }; 237 | name = Release; 238 | }; 239 | B6AF40A01B4FE3260060F1B8 /* Debug */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | DEFINES_MODULE = YES; 243 | DYLIB_COMPATIBILITY_VERSION = 1; 244 | DYLIB_CURRENT_VERSION = 1; 245 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 246 | INFOPLIST_FILE = HCSStarRatingView/Info.plist; 247 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 248 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 249 | PRODUCT_NAME = "$(TARGET_NAME)"; 250 | SKIP_INSTALL = YES; 251 | }; 252 | name = Debug; 253 | }; 254 | B6AF40A11B4FE3260060F1B8 /* Release */ = { 255 | isa = XCBuildConfiguration; 256 | buildSettings = { 257 | DEFINES_MODULE = YES; 258 | DYLIB_COMPATIBILITY_VERSION = 1; 259 | DYLIB_CURRENT_VERSION = 1; 260 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 261 | INFOPLIST_FILE = HCSStarRatingView/Info.plist; 262 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 263 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 264 | PRODUCT_NAME = "$(TARGET_NAME)"; 265 | SKIP_INSTALL = YES; 266 | }; 267 | name = Release; 268 | }; 269 | /* End XCBuildConfiguration section */ 270 | 271 | /* Begin XCConfigurationList section */ 272 | B6AF40831B4FE3260060F1B8 /* Build configuration list for PBXProject "HCSStarRatingView" */ = { 273 | isa = XCConfigurationList; 274 | buildConfigurations = ( 275 | B6AF409D1B4FE3260060F1B8 /* Debug */, 276 | B6AF409E1B4FE3260060F1B8 /* Release */, 277 | ); 278 | defaultConfigurationIsVisible = 0; 279 | defaultConfigurationName = Release; 280 | }; 281 | B6AF409F1B4FE3260060F1B8 /* Build configuration list for PBXNativeTarget "HCSStarRatingView" */ = { 282 | isa = XCConfigurationList; 283 | buildConfigurations = ( 284 | B6AF40A01B4FE3260060F1B8 /* Debug */, 285 | B6AF40A11B4FE3260060F1B8 /* Release */, 286 | ); 287 | defaultConfigurationIsVisible = 0; 288 | defaultConfigurationName = Release; 289 | }; 290 | /* End XCConfigurationList section */ 291 | }; 292 | rootObject = B6AF40801B4FE3260060F1B8 /* Project object */; 293 | } 294 | -------------------------------------------------------------------------------- /HCSStarRatingView.xcodeproj/xcshareddata/xcschemes/HCSStarRatingView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /HCSStarRatingView/HCSStarRatingView.h: -------------------------------------------------------------------------------- 1 | // HCSStarRatingView.h 2 | // 3 | // Copyright (c) 2015 Hugo Sousa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | @import UIKit; 24 | 25 | typedef BOOL(^HCSStarRatingViewShouldBeginGestureRecognizerBlock)(UIGestureRecognizer *gestureRecognizer); 26 | 27 | IB_DESIGNABLE 28 | @interface HCSStarRatingView : UIControl 29 | @property (nonatomic) IBInspectable NSUInteger maximumValue; 30 | @property (nonatomic) IBInspectable CGFloat minimumValue; 31 | @property (nonatomic) IBInspectable CGFloat value; 32 | @property (nonatomic) IBInspectable CGFloat spacing; 33 | @property (nonatomic) IBInspectable BOOL allowsHalfStars; 34 | @property (nonatomic) IBInspectable BOOL accurateHalfStars; 35 | @property (nonatomic) IBInspectable BOOL continuous; 36 | 37 | @property (nonatomic) BOOL shouldBecomeFirstResponder; 38 | 39 | // Optional: if `nil` method will return `NO`. 40 | @property (nonatomic, copy) HCSStarRatingViewShouldBeginGestureRecognizerBlock shouldBeginGestureRecognizerBlock; 41 | 42 | @property (nonatomic, strong) IBInspectable UIColor *starBorderColor; 43 | @property (nonatomic) IBInspectable CGFloat starBorderWidth; 44 | 45 | @property (nonatomic, strong) IBInspectable UIColor *emptyStarColor; 46 | 47 | @property (nonatomic, strong) IBInspectable UIImage *emptyStarImage; 48 | @property (nonatomic, strong) IBInspectable UIImage *halfStarImage; 49 | @property (nonatomic, strong) IBInspectable UIImage *filledStarImage; 50 | @end 51 | 52 | -------------------------------------------------------------------------------- /HCSStarRatingView/HCSStarRatingView.m: -------------------------------------------------------------------------------- 1 | // HCSStarRatingView.m 2 | // 3 | // Copyright (c) 2015 Hugo Sousa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "HCSStarRatingView.h" 24 | 25 | @interface HCSStarRatingView () 26 | @property (nonatomic, readonly) BOOL shouldUseImages; 27 | @end 28 | 29 | @implementation HCSStarRatingView { 30 | CGFloat _minimumValue; 31 | NSUInteger _maximumValue; 32 | CGFloat _value; 33 | UIColor *_starBorderColor; 34 | } 35 | 36 | @dynamic minimumValue; 37 | @dynamic maximumValue; 38 | @dynamic value; 39 | @dynamic shouldUseImages; 40 | @dynamic starBorderColor; 41 | 42 | #pragma mark - Initialization 43 | 44 | - (instancetype)initWithFrame:(CGRect)frame { 45 | self = [super initWithFrame:frame]; 46 | if (self) { 47 | [self _customInit]; 48 | } 49 | return self; 50 | } 51 | 52 | - (id)initWithCoder:(NSCoder *)aDecoder { 53 | self = [super initWithCoder:aDecoder]; 54 | if (self) { 55 | [self _customInit]; 56 | } 57 | return self; 58 | } 59 | 60 | - (void)_customInit { 61 | self.exclusiveTouch = YES; 62 | _minimumValue = 0; 63 | _maximumValue = 5; 64 | _value = 0; 65 | _spacing = 5.f; 66 | _continuous = YES; 67 | _starBorderWidth = 1.0f; 68 | _emptyStarColor = [UIColor clearColor]; 69 | 70 | [self _updateAppearanceForState:self.enabled]; 71 | } 72 | 73 | - (void)setNeedsLayout { 74 | [super setNeedsLayout]; 75 | [self setNeedsDisplay]; 76 | } 77 | 78 | #pragma mark - Properties 79 | 80 | - (UIColor *)backgroundColor { 81 | if ([super backgroundColor]) { 82 | return [super backgroundColor]; 83 | } else { 84 | return self.isOpaque ? [UIColor whiteColor] : [UIColor clearColor]; 85 | }; 86 | } 87 | 88 | - (CGFloat)minimumValue { 89 | return MAX(_minimumValue, 0); 90 | } 91 | 92 | - (void)setMinimumValue:(CGFloat)minimumValue { 93 | if (_minimumValue != minimumValue) { 94 | _minimumValue = minimumValue; 95 | [self setNeedsDisplay]; 96 | } 97 | } 98 | 99 | - (NSUInteger)maximumValue { 100 | return MAX(_minimumValue, _maximumValue); 101 | } 102 | 103 | - (void)setMaximumValue:(NSUInteger)maximumValue { 104 | if (_maximumValue != maximumValue) { 105 | _maximumValue = maximumValue; 106 | [self setNeedsDisplay]; 107 | [self invalidateIntrinsicContentSize]; 108 | } 109 | } 110 | 111 | - (CGFloat)value { 112 | return MIN(MAX(_value, _minimumValue), _maximumValue); 113 | } 114 | 115 | - (void)setValue:(CGFloat)value { 116 | [self setValue:value sendValueChangedAction:NO]; 117 | } 118 | 119 | - (void)setValue:(CGFloat)value sendValueChangedAction:(BOOL)sendAction { 120 | [self willChangeValueForKey:NSStringFromSelector(@selector(value))]; 121 | if (_value != value && value >= _minimumValue && value <= _maximumValue) { 122 | _value = value; 123 | if (sendAction) [self sendActionsForControlEvents:UIControlEventValueChanged]; 124 | [self setNeedsDisplay]; 125 | } 126 | [self didChangeValueForKey:NSStringFromSelector(@selector(value))]; 127 | } 128 | 129 | - (void)setSpacing:(CGFloat)spacing { 130 | _spacing = MAX(spacing, 0); 131 | [self setNeedsDisplay]; 132 | } 133 | 134 | - (void)setAllowsHalfStars:(BOOL)allowsHalfStars { 135 | if (_allowsHalfStars != allowsHalfStars) { 136 | _allowsHalfStars = allowsHalfStars; 137 | [self setNeedsDisplay]; 138 | } 139 | } 140 | 141 | - (void)setAccurateHalfStars:(BOOL)accurateHalfStars { 142 | if (_accurateHalfStars != accurateHalfStars) { 143 | _accurateHalfStars = accurateHalfStars; 144 | [self setNeedsDisplay]; 145 | } 146 | } 147 | 148 | - (void)setEmptyStarImage:(UIImage *)emptyStarImage { 149 | if (_emptyStarImage != emptyStarImage) { 150 | _emptyStarImage = emptyStarImage; 151 | [self setNeedsDisplay]; 152 | } 153 | } 154 | 155 | - (void)setHalfStarImage:(UIImage *)halfStarImage { 156 | if (_halfStarImage != halfStarImage) { 157 | _halfStarImage = halfStarImage; 158 | [self setNeedsDisplay]; 159 | } 160 | } 161 | 162 | - (void)setFilledStarImage:(UIImage *)filledStarImage { 163 | if (_filledStarImage != filledStarImage) { 164 | _filledStarImage = filledStarImage; 165 | [self setNeedsDisplay]; 166 | } 167 | } 168 | 169 | - (void)setEmptyStarColor:(UIColor *)emptyStarColor { 170 | if (_emptyStarColor != emptyStarColor) { 171 | _emptyStarColor = emptyStarColor; 172 | [self setNeedsDisplay]; 173 | } 174 | } 175 | 176 | - (void)setStarBorderColor:(UIColor *)starBorderColor { 177 | if (_starBorderColor != starBorderColor) { 178 | _starBorderColor = starBorderColor; 179 | [self setNeedsDisplay]; 180 | } 181 | } 182 | 183 | - (UIColor *)starBorderColor { 184 | if (_starBorderColor == nil) { 185 | return self.tintColor; 186 | } else { 187 | return _starBorderColor; 188 | } 189 | } 190 | 191 | - (void)setStarBorderWidth:(CGFloat)starBorderWidth { 192 | _starBorderWidth = MAX(0, starBorderWidth); 193 | [self setNeedsDisplay]; 194 | } 195 | 196 | 197 | - (BOOL)shouldUseImages { 198 | return (self.emptyStarImage!=nil && self.filledStarImage!=nil); 199 | } 200 | 201 | #pragma mark - State 202 | 203 | - (void)setEnabled:(BOOL)enabled 204 | { 205 | [self _updateAppearanceForState:enabled]; 206 | [super setEnabled:enabled]; 207 | } 208 | 209 | - (void)_updateAppearanceForState:(BOOL)enabled 210 | { 211 | self.alpha = enabled ? 1.f : .5f; 212 | } 213 | 214 | #pragma mark - Image Drawing 215 | 216 | - (void)_drawStarImageWithFrame:(CGRect)frame tintColor:(UIColor*)tintColor highlighted:(BOOL)highlighted { 217 | UIImage *image = highlighted ? self.filledStarImage : self.emptyStarImage; 218 | [self _drawImage:image frame:frame tintColor:tintColor]; 219 | } 220 | 221 | - (void)_drawHalfStarImageWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor { 222 | [self _drawAccurateHalfStarImageWithFrame:frame tintColor:tintColor progress:.5f]; 223 | } 224 | 225 | - (void)_drawAccurateHalfStarImageWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor progress:(CGFloat)progress { 226 | UIImage *image = self.halfStarImage; 227 | if (image == nil) { 228 | // first draw star outline 229 | [self _drawStarImageWithFrame:frame tintColor:tintColor highlighted:NO]; 230 | 231 | image = self.filledStarImage; 232 | CGRect imageFrame = CGRectMake(0, 0, image.size.width * image.scale * progress, image.size.height * image.scale); 233 | frame.size.width *= progress; 234 | CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, imageFrame); 235 | UIImage *halfImage = [UIImage imageWithCGImage:imageRef scale:image.scale orientation:image.imageOrientation]; 236 | image = [halfImage imageWithRenderingMode:image.renderingMode]; 237 | CGImageRelease(imageRef); 238 | } 239 | [self _drawImage:image frame:frame tintColor:tintColor]; 240 | } 241 | 242 | - (void)_drawImage:(UIImage *)image frame:(CGRect)frame tintColor:(UIColor *)tintColor { 243 | if (image.renderingMode == UIImageRenderingModeAlwaysTemplate) { 244 | [tintColor setFill]; 245 | } 246 | [image drawInRect:frame]; 247 | } 248 | 249 | #pragma mark - Shape Drawing 250 | 251 | - (void)_drawStarShapeWithFrame:(CGRect)frame tintColor:(UIColor*)tintColor highlighted:(BOOL)highlighted { 252 | [self _drawAccurateHalfStarShapeWithFrame:frame tintColor:tintColor progress:highlighted ? 1.f : 0.f]; 253 | } 254 | 255 | - (void)_drawHalfStarShapeWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor { 256 | [self _drawAccurateHalfStarShapeWithFrame:frame tintColor:tintColor progress:.5f]; 257 | } 258 | 259 | - (void)_drawAccurateHalfStarShapeWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor progress:(CGFloat)progress { 260 | UIBezierPath* starShapePath = UIBezierPath.bezierPath; 261 | [starShapePath moveToPoint: CGPointMake(CGRectGetMinX(frame) + 0.62723 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.37309 * CGRectGetHeight(frame))]; 262 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.50000 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.02500 * CGRectGetHeight(frame))]; 263 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.37292 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.37309 * CGRectGetHeight(frame))]; 264 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.02500 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.39112 * CGRectGetHeight(frame))]; 265 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.30504 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.62908 * CGRectGetHeight(frame))]; 266 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.20642 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.97500 * CGRectGetHeight(frame))]; 267 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.50000 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.78265 * CGRectGetHeight(frame))]; 268 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.79358 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.97500 * CGRectGetHeight(frame))]; 269 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.69501 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.62908 * CGRectGetHeight(frame))]; 270 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.97500 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.39112 * CGRectGetHeight(frame))]; 271 | [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.62723 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.37309 * CGRectGetHeight(frame))]; 272 | [starShapePath closePath]; 273 | starShapePath.miterLimit = 4; 274 | 275 | CGFloat frameWidth = frame.size.width; 276 | CGRect rightRectOfStar = CGRectMake(frame.origin.x + progress * frameWidth, frame.origin.y, frameWidth - progress * frameWidth, frame.size.height); 277 | UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectInfinite]; 278 | [clipPath appendPath:[UIBezierPath bezierPathWithRect:rightRectOfStar]]; 279 | clipPath.usesEvenOddFillRule = YES; 280 | 281 | [_emptyStarColor setFill]; 282 | [starShapePath fill]; 283 | 284 | CGContextSaveGState(UIGraphicsGetCurrentContext()); { 285 | [clipPath addClip]; 286 | [tintColor setFill]; 287 | [starShapePath fill]; 288 | } 289 | CGContextRestoreGState(UIGraphicsGetCurrentContext()); 290 | 291 | [self.starBorderColor setStroke]; 292 | starShapePath.lineWidth = _starBorderWidth; 293 | [starShapePath stroke]; 294 | } 295 | 296 | #pragma mark - Drawing 297 | 298 | - (void)drawRect:(CGRect)rect { 299 | CGContextRef context = UIGraphicsGetCurrentContext(); 300 | CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor); 301 | CGContextFillRect(context, rect); 302 | 303 | CGFloat availableWidth = rect.size.width - (_spacing * (_maximumValue - 1)) - 2; 304 | CGFloat cellWidth = (availableWidth / _maximumValue); 305 | CGFloat starSide = (cellWidth <= rect.size.height) ? cellWidth : rect.size.height; 306 | starSide = (self.shouldUseImages) ? starSide : (starSide - _starBorderWidth); 307 | 308 | for (int idx = 0; idx < _maximumValue; idx++) { 309 | CGPoint center = CGPointMake(cellWidth*idx + cellWidth/2 + _spacing*idx + 1, rect.size.height/2); 310 | CGRect frame = CGRectMake(center.x - starSide/2, center.y - starSide/2, starSide, starSide); 311 | BOOL highlighted = (idx+1 <= ceilf(_value)); 312 | if (_allowsHalfStars && highlighted && (idx+1 > _value)) { 313 | if (_accurateHalfStars) { 314 | [self _drawAccurateStarWithFrame:frame tintColor:self.tintColor progress:_value - idx]; 315 | } 316 | else { 317 | [self _drawHalfStarWithFrame:frame tintColor:self.tintColor]; 318 | } 319 | } else { 320 | [self _drawStarWithFrame:frame tintColor:self.tintColor highlighted:highlighted]; 321 | } 322 | } 323 | } 324 | 325 | - (void)_drawStarWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor highlighted:(BOOL)highlighted { 326 | if (self.shouldUseImages) { 327 | [self _drawStarImageWithFrame:frame tintColor:tintColor highlighted:highlighted]; 328 | } else { 329 | [self _drawStarShapeWithFrame:frame tintColor:tintColor highlighted:highlighted]; 330 | } 331 | } 332 | 333 | - (void)_drawHalfStarWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor { 334 | if (self.shouldUseImages) { 335 | [self _drawHalfStarImageWithFrame:frame tintColor:tintColor]; 336 | } else { 337 | [self _drawHalfStarShapeWithFrame:frame tintColor:tintColor]; 338 | } 339 | } 340 | - (void)_drawAccurateStarWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor progress:(CGFloat)progress { 341 | if (self.shouldUseImages) { 342 | [self _drawAccurateHalfStarImageWithFrame:frame tintColor:tintColor progress:progress]; 343 | } else { 344 | [self _drawAccurateHalfStarShapeWithFrame:frame tintColor:tintColor progress:progress]; 345 | } 346 | } 347 | #pragma mark - Touches 348 | 349 | - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 350 | if (self.isEnabled) { 351 | [super beginTrackingWithTouch:touch withEvent:event]; 352 | if (_shouldBecomeFirstResponder && ![self isFirstResponder]) { 353 | [self becomeFirstResponder]; 354 | } 355 | [self _handleTouch:touch]; 356 | return YES; 357 | } else { 358 | return NO; 359 | } 360 | } 361 | 362 | - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 363 | if (self.isEnabled) { 364 | [super continueTrackingWithTouch:touch withEvent:event]; 365 | [self _handleTouch:touch]; 366 | return YES; 367 | } else { 368 | return NO; 369 | } 370 | } 371 | 372 | - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 373 | [super endTrackingWithTouch:touch withEvent:event]; 374 | if (_shouldBecomeFirstResponder && [self isFirstResponder]) { 375 | [self resignFirstResponder]; 376 | } 377 | [self _handleTouch:touch]; 378 | if (!_continuous) { 379 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 380 | } 381 | } 382 | 383 | - (void)cancelTrackingWithEvent:(UIEvent *)event { 384 | [super cancelTrackingWithEvent:event]; 385 | if (_shouldBecomeFirstResponder && [self isFirstResponder]) { 386 | [self resignFirstResponder]; 387 | } 388 | } 389 | 390 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { 391 | if ([gestureRecognizer.view isEqual:self]) { 392 | return !self.isUserInteractionEnabled; 393 | } 394 | return self.shouldBeginGestureRecognizerBlock ? self.shouldBeginGestureRecognizerBlock(gestureRecognizer) : NO; 395 | } 396 | 397 | - (void)_handleTouch:(UITouch *)touch { 398 | CGFloat cellWidth = self.bounds.size.width / _maximumValue; 399 | CGPoint location = [touch locationInView:self]; 400 | CGFloat value = location.x / cellWidth; 401 | if (_allowsHalfStars) { 402 | if (_accurateHalfStars) { 403 | value = value; 404 | } 405 | else { 406 | if (value+.5f < ceilf(value)) { 407 | value = floor(value)+.5f; 408 | } else { 409 | value = ceilf(value); 410 | } 411 | } 412 | } else { 413 | value = ceilf(value); 414 | } 415 | [self setValue:value sendValueChangedAction:_continuous]; 416 | } 417 | 418 | #pragma mark - First responder 419 | 420 | - (BOOL)canBecomeFirstResponder { 421 | return _shouldBecomeFirstResponder; 422 | } 423 | 424 | #pragma mark - Intrinsic Content Size 425 | 426 | - (CGSize)intrinsicContentSize { 427 | CGFloat height = 44.f; 428 | return CGSizeMake(_maximumValue * height + (_maximumValue-1) * _spacing, height); 429 | } 430 | 431 | #pragma mark - Accessibility 432 | 433 | - (BOOL)isAccessibilityElement { 434 | return YES; 435 | } 436 | 437 | - (NSString *)accessibilityLabel { 438 | return [super accessibilityLabel] ?: NSLocalizedString(@"Rating", @"Accessibility label for star rating control."); 439 | } 440 | 441 | - (UIAccessibilityTraits)accessibilityTraits { 442 | return ([super accessibilityTraits] | UIAccessibilityTraitAdjustable); 443 | } 444 | 445 | - (NSString *)accessibilityValue { 446 | return [@(self.value) description]; 447 | } 448 | 449 | - (BOOL)accessibilityActivate { 450 | return YES; 451 | } 452 | 453 | - (void)accessibilityIncrement { 454 | CGFloat value = self.value + (self.allowsHalfStars ? .5f : 1.f); 455 | [self setValue:value sendValueChangedAction:YES]; 456 | } 457 | 458 | - (void)accessibilityDecrement { 459 | CGFloat value = self.value - (self.allowsHalfStars ? .5f : 1.f); 460 | [self setValue:value sendValueChangedAction:YES]; 461 | } 462 | 463 | @end 464 | -------------------------------------------------------------------------------- /HCSStarRatingView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.hsousa.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Hugo Sousa 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "HCSStarRatingView", 7 | products: [ 8 | .library(name: "HCSStarRatingView", targets: ["HCSStarRatingView"]) 9 | ], 10 | targets: [ 11 | .target( 12 | name: "HCSStarRatingView", 13 | path: "HCSStarRatingView", 14 | exclude: ["Info.plist"], 15 | publicHeadersPath: "", 16 | cSettings: [ 17 | .headerSearchPath("") 18 | ] 19 | ) 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HCSStarRatingView 2 | 3 | `HCSStarRatingView` is a `UIControl` subclass to easily provide users with a basic star rating interface. 4 | 5 | It supports all device resolutions and although it requires no images to render the stars (thanks PaintCode), you can provide custom ones if you so desire. 6 | 7 | 8 | 9 | ## Installation 10 | 11 | ### Carthage 12 | 13 | ``` 14 | github "hsousa/HCSStarRatingView" 15 | ``` 16 | 17 | ### CocoaPods 18 | 19 | ``` 20 | use_frameworks! 21 | 22 | (...) 23 | 24 | pod 'HCSStarRatingView', '~> 1.5' 25 | ``` 26 | 27 | and run `pod install` 28 | 29 | ### Manually 30 | 31 | You can also install it manually by copying `HCSStarRatingView.{h|m}` into your project or including the project in your own project/workspace, similar to what's being done in `Sample`. 32 | 33 | ## Usage 34 | 35 | ### Programatically 36 | 37 | Create a new instance and set the properties you desire to customize. 38 | 39 | ```objective-c 40 | HCSStarRatingView *starRatingView = [[HCSStarRatingView alloc] initWithFrame:CGRectMake(50, 200, 200, 50)]; 41 | starRatingView.maximumValue = 10; 42 | starRatingView.minimumValue = 0; 43 | starRatingView.value = 0; 44 | starRatingView.tintColor = [UIColor redColor]; 45 | [starRatingView addTarget:self action:@selector(didChangeValue:) forControlEvents:UIControlEventValueChanged]; 46 | [self.view addSubview:starRatingView]; 47 | ``` 48 | 49 | `HCSStarRatingView` also works great with Auto Layout, so feel free to set some constraints instead of just giving it a frame (check sample project)! 50 | 51 | #### Half-star ratings: 52 | 53 | ```objective-c 54 | starRatingView.allowsHalfStars = YES; 55 | starRatingView.value = 2.5f; 56 | ``` 57 | 58 | Enable `accurateHalfStars` to get more precise ratings (works with images too)! 59 | ```objective-c 60 | starRatingView.accurateHalfStars = YES; 61 | ``` 62 | 63 | #### Custom images: 64 | 65 | Using custom images in `HCSStarRatingView` is as easy as setting a property. You only need to set `emptyStarImage` and `filledStarImage`, but you can also provide the half image to `halfStarImage`, if your design requires you to: 66 | 67 | ```objective-c 68 | starRatingView.emptyStarImage = [UIImage imageNamed:@"heart-empty"]; 69 | starRatingView.halfStarImage = [UIImage imageNamed:@"heart-half"]; // optional 70 | starRatingView.filledStarImage = [UIImage imageNamed:@"heart-full"]; 71 | ``` 72 | 73 | If you want to use template images programatically, just make sure they have the proper `Rendering Mode`: 74 | 75 | ```objective-c 76 | starRatingView.emptyStarImage = [[UIImage imageNamed:@"heart-empty"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 77 | starRatingView.halfStarImage = [[UIImage imageNamed:@"heart-half"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; // optional 78 | starRatingView.filledStarImage = [[UIImage imageNamed:@"heart-full"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 79 | ``` 80 | 81 | ### Interface Builder 82 | 83 | `HCSStarRatingView` also implements `IB_DESIGNABLE` and `IBInspectable` so you can fully customize it in Interface Builder. 84 | 85 | 86 | 87 | PS: In order to use template images in Interface Builder you must go to that image's properties in your Asset Catalog and change the `Render As` setting to `Template Image`. 88 | 89 | ## Accessibility 90 | 91 | Users with specific needs should be able to fully read and interact with `HCSStarRatingView`, since it fully supports VoiceOver. 92 | 93 | If you or your users have other specific needs and you're having issues with this control, please contact me so we can figure it out! :-) 94 | 95 | ## Contact 96 | Hugo Sousa 97 | * [github.com/hsousa](http://github.com/hsousa) 98 | * [twitter/hsousa](http://twitter.com/hsousa) 99 | * [hsousa@me.com](hsousa@me.com) 100 | 101 | ## License 102 | HCSStarRatingView is available under the MIT license. See the LICENSE file for more info. 103 | -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 56B1A7CC1A8A4240008C5470 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 56B1A7CB1A8A4240008C5470 /* main.m */; }; 11 | 56B1A7CF1A8A4240008C5470 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 56B1A7CE1A8A4240008C5470 /* AppDelegate.m */; }; 12 | 56B1A7D21A8A4240008C5470 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 56B1A7D11A8A4240008C5470 /* ViewController.m */; }; 13 | 56B1A7D51A8A4240008C5470 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 56B1A7D31A8A4240008C5470 /* Main.storyboard */; }; 14 | 56B1A7D71A8A4240008C5470 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 56B1A7D61A8A4240008C5470 /* Images.xcassets */; }; 15 | 56B1A7DA1A8A4240008C5470 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56B1A7D81A8A4240008C5470 /* LaunchScreen.xib */; }; 16 | B6AF40CC1B4FE7450060F1B8 /* HCSStarRatingView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6AF40C41B4FE7120060F1B8 /* HCSStarRatingView.framework */; }; 17 | B6AF40CD1B4FE7450060F1B8 /* HCSStarRatingView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B6AF40C41B4FE7120060F1B8 /* HCSStarRatingView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | B6AF40C31B4FE7120060F1B8 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = B6AF40BE1B4FE7120060F1B8 /* HCSStarRatingView.xcodeproj */; 24 | proxyType = 2; 25 | remoteGlobalIDString = B6AF40891B4FE3260060F1B8; 26 | remoteInfo = HCSStarRatingView; 27 | }; 28 | B6AF40CE1B4FE7450060F1B8 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = B6AF40BE1B4FE7120060F1B8 /* HCSStarRatingView.xcodeproj */; 31 | proxyType = 1; 32 | remoteGlobalIDString = B6AF40881B4FE3260060F1B8; 33 | remoteInfo = HCSStarRatingView; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXCopyFilesBuildPhase section */ 38 | B6AF40D01B4FE7450060F1B8 /* Embed Frameworks */ = { 39 | isa = PBXCopyFilesBuildPhase; 40 | buildActionMask = 2147483647; 41 | dstPath = ""; 42 | dstSubfolderSpec = 10; 43 | files = ( 44 | B6AF40CD1B4FE7450060F1B8 /* HCSStarRatingView.framework in Embed Frameworks */, 45 | ); 46 | name = "Embed Frameworks"; 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXCopyFilesBuildPhase section */ 50 | 51 | /* Begin PBXFileReference section */ 52 | 56B1A7C61A8A4240008C5470 /* HCSStarRatingViewSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HCSStarRatingViewSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 56B1A7CA1A8A4240008C5470 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 56B1A7CB1A8A4240008C5470 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | 56B1A7CD1A8A4240008C5470 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 56 | 56B1A7CE1A8A4240008C5470 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 57 | 56B1A7D01A8A4240008C5470 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 58 | 56B1A7D11A8A4240008C5470 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 59 | 56B1A7D41A8A4240008C5470 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 60 | 56B1A7D61A8A4240008C5470 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 61 | 56B1A7D91A8A4240008C5470 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 62 | B6AF40BE1B4FE7120060F1B8 /* HCSStarRatingView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = HCSStarRatingView.xcodeproj; path = ../HCSStarRatingView.xcodeproj; sourceTree = ""; }; 63 | /* End PBXFileReference section */ 64 | 65 | /* Begin PBXFrameworksBuildPhase section */ 66 | 56B1A7C31A8A4240008C5470 /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | B6AF40CC1B4FE7450060F1B8 /* HCSStarRatingView.framework in Frameworks */, 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | 56B1A7BD1A8A4240008C5470 = { 78 | isa = PBXGroup; 79 | children = ( 80 | 56B1A7C81A8A4240008C5470 /* HCSStarRatingViewSample */, 81 | 56B1A7C71A8A4240008C5470 /* Products */, 82 | B6AF40BE1B4FE7120060F1B8 /* HCSStarRatingView.xcodeproj */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | 56B1A7C71A8A4240008C5470 /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 56B1A7C61A8A4240008C5470 /* HCSStarRatingViewSample.app */, 90 | ); 91 | name = Products; 92 | sourceTree = ""; 93 | }; 94 | 56B1A7C81A8A4240008C5470 /* HCSStarRatingViewSample */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 56B1A7CD1A8A4240008C5470 /* AppDelegate.h */, 98 | 56B1A7CE1A8A4240008C5470 /* AppDelegate.m */, 99 | 56B1A7D01A8A4240008C5470 /* ViewController.h */, 100 | 56B1A7D11A8A4240008C5470 /* ViewController.m */, 101 | 56B1A7D31A8A4240008C5470 /* Main.storyboard */, 102 | 56B1A7D61A8A4240008C5470 /* Images.xcassets */, 103 | 56B1A7D81A8A4240008C5470 /* LaunchScreen.xib */, 104 | 56B1A7C91A8A4240008C5470 /* Supporting Files */, 105 | ); 106 | path = HCSStarRatingViewSample; 107 | sourceTree = ""; 108 | }; 109 | 56B1A7C91A8A4240008C5470 /* Supporting Files */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 56B1A7CA1A8A4240008C5470 /* Info.plist */, 113 | 56B1A7CB1A8A4240008C5470 /* main.m */, 114 | ); 115 | name = "Supporting Files"; 116 | sourceTree = ""; 117 | }; 118 | B6AF40BF1B4FE7120060F1B8 /* Products */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | B6AF40C41B4FE7120060F1B8 /* HCSStarRatingView.framework */, 122 | ); 123 | name = Products; 124 | sourceTree = ""; 125 | }; 126 | /* End PBXGroup section */ 127 | 128 | /* Begin PBXNativeTarget section */ 129 | 56B1A7C51A8A4240008C5470 /* HCSStarRatingViewSample */ = { 130 | isa = PBXNativeTarget; 131 | buildConfigurationList = 56B1A7E91A8A4241008C5470 /* Build configuration list for PBXNativeTarget "HCSStarRatingViewSample" */; 132 | buildPhases = ( 133 | 56B1A7C21A8A4240008C5470 /* Sources */, 134 | 56B1A7C31A8A4240008C5470 /* Frameworks */, 135 | 56B1A7C41A8A4240008C5470 /* Resources */, 136 | B6AF40D01B4FE7450060F1B8 /* Embed Frameworks */, 137 | ); 138 | buildRules = ( 139 | ); 140 | dependencies = ( 141 | B6AF40CF1B4FE7450060F1B8 /* PBXTargetDependency */, 142 | ); 143 | name = HCSStarRatingViewSample; 144 | productName = HCSStarRatingViewSample; 145 | productReference = 56B1A7C61A8A4240008C5470 /* HCSStarRatingViewSample.app */; 146 | productType = "com.apple.product-type.application"; 147 | }; 148 | /* End PBXNativeTarget section */ 149 | 150 | /* Begin PBXProject section */ 151 | 56B1A7BE1A8A4240008C5470 /* Project object */ = { 152 | isa = PBXProject; 153 | attributes = { 154 | LastUpgradeCheck = 0610; 155 | ORGANIZATIONNAME = "Hugo Sousa"; 156 | TargetAttributes = { 157 | 56B1A7C51A8A4240008C5470 = { 158 | CreatedOnToolsVersion = 6.1.1; 159 | }; 160 | }; 161 | }; 162 | buildConfigurationList = 56B1A7C11A8A4240008C5470 /* Build configuration list for PBXProject "HCSStarRatingViewSample" */; 163 | compatibilityVersion = "Xcode 3.2"; 164 | developmentRegion = English; 165 | hasScannedForEncodings = 0; 166 | knownRegions = ( 167 | en, 168 | Base, 169 | ); 170 | mainGroup = 56B1A7BD1A8A4240008C5470; 171 | productRefGroup = 56B1A7C71A8A4240008C5470 /* Products */; 172 | projectDirPath = ""; 173 | projectReferences = ( 174 | { 175 | ProductGroup = B6AF40BF1B4FE7120060F1B8 /* Products */; 176 | ProjectRef = B6AF40BE1B4FE7120060F1B8 /* HCSStarRatingView.xcodeproj */; 177 | }, 178 | ); 179 | projectRoot = ""; 180 | targets = ( 181 | 56B1A7C51A8A4240008C5470 /* HCSStarRatingViewSample */, 182 | ); 183 | }; 184 | /* End PBXProject section */ 185 | 186 | /* Begin PBXReferenceProxy section */ 187 | B6AF40C41B4FE7120060F1B8 /* HCSStarRatingView.framework */ = { 188 | isa = PBXReferenceProxy; 189 | fileType = wrapper.framework; 190 | path = HCSStarRatingView.framework; 191 | remoteRef = B6AF40C31B4FE7120060F1B8 /* PBXContainerItemProxy */; 192 | sourceTree = BUILT_PRODUCTS_DIR; 193 | }; 194 | /* End PBXReferenceProxy section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 56B1A7C41A8A4240008C5470 /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 56B1A7D51A8A4240008C5470 /* Main.storyboard in Resources */, 202 | 56B1A7DA1A8A4240008C5470 /* LaunchScreen.xib in Resources */, 203 | 56B1A7D71A8A4240008C5470 /* Images.xcassets in Resources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXResourcesBuildPhase section */ 208 | 209 | /* Begin PBXSourcesBuildPhase section */ 210 | 56B1A7C21A8A4240008C5470 /* Sources */ = { 211 | isa = PBXSourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | 56B1A7D21A8A4240008C5470 /* ViewController.m in Sources */, 215 | 56B1A7CF1A8A4240008C5470 /* AppDelegate.m in Sources */, 216 | 56B1A7CC1A8A4240008C5470 /* main.m in Sources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXSourcesBuildPhase section */ 221 | 222 | /* Begin PBXTargetDependency section */ 223 | B6AF40CF1B4FE7450060F1B8 /* PBXTargetDependency */ = { 224 | isa = PBXTargetDependency; 225 | name = HCSStarRatingView; 226 | targetProxy = B6AF40CE1B4FE7450060F1B8 /* PBXContainerItemProxy */; 227 | }; 228 | /* End PBXTargetDependency section */ 229 | 230 | /* Begin PBXVariantGroup section */ 231 | 56B1A7D31A8A4240008C5470 /* Main.storyboard */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | 56B1A7D41A8A4240008C5470 /* Base */, 235 | ); 236 | name = Main.storyboard; 237 | sourceTree = ""; 238 | }; 239 | 56B1A7D81A8A4240008C5470 /* LaunchScreen.xib */ = { 240 | isa = PBXVariantGroup; 241 | children = ( 242 | 56B1A7D91A8A4240008C5470 /* Base */, 243 | ); 244 | name = LaunchScreen.xib; 245 | sourceTree = ""; 246 | }; 247 | /* End PBXVariantGroup section */ 248 | 249 | /* Begin XCBuildConfiguration section */ 250 | 56B1A7E71A8A4241008C5470 /* Debug */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | ALWAYS_SEARCH_USER_PATHS = NO; 254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 255 | CLANG_CXX_LIBRARY = "libc++"; 256 | CLANG_ENABLE_MODULES = YES; 257 | CLANG_ENABLE_OBJC_ARC = YES; 258 | CLANG_WARN_BOOL_CONVERSION = YES; 259 | CLANG_WARN_CONSTANT_CONVERSION = YES; 260 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 261 | CLANG_WARN_EMPTY_BODY = YES; 262 | CLANG_WARN_ENUM_CONVERSION = YES; 263 | CLANG_WARN_INT_CONVERSION = YES; 264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 265 | CLANG_WARN_UNREACHABLE_CODE = YES; 266 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 267 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 268 | COPY_PHASE_STRIP = NO; 269 | ENABLE_STRICT_OBJC_MSGSEND = YES; 270 | GCC_C_LANGUAGE_STANDARD = gnu99; 271 | GCC_DYNAMIC_NO_PIC = NO; 272 | GCC_OPTIMIZATION_LEVEL = 0; 273 | GCC_PREPROCESSOR_DEFINITIONS = ( 274 | "DEBUG=1", 275 | "$(inherited)", 276 | ); 277 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 280 | GCC_WARN_UNDECLARED_SELECTOR = YES; 281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 282 | GCC_WARN_UNUSED_FUNCTION = YES; 283 | GCC_WARN_UNUSED_VARIABLE = YES; 284 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 285 | MTL_ENABLE_DEBUG_INFO = YES; 286 | ONLY_ACTIVE_ARCH = YES; 287 | SDKROOT = iphoneos; 288 | }; 289 | name = Debug; 290 | }; 291 | 56B1A7E81A8A4241008C5470 /* Release */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ALWAYS_SEARCH_USER_PATHS = NO; 295 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 296 | CLANG_CXX_LIBRARY = "libc++"; 297 | CLANG_ENABLE_MODULES = YES; 298 | CLANG_ENABLE_OBJC_ARC = YES; 299 | CLANG_WARN_BOOL_CONVERSION = YES; 300 | CLANG_WARN_CONSTANT_CONVERSION = YES; 301 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 302 | CLANG_WARN_EMPTY_BODY = YES; 303 | CLANG_WARN_ENUM_CONVERSION = YES; 304 | CLANG_WARN_INT_CONVERSION = YES; 305 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 306 | CLANG_WARN_UNREACHABLE_CODE = YES; 307 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 308 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 309 | COPY_PHASE_STRIP = YES; 310 | ENABLE_NS_ASSERTIONS = NO; 311 | ENABLE_STRICT_OBJC_MSGSEND = YES; 312 | GCC_C_LANGUAGE_STANDARD = gnu99; 313 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 314 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 315 | GCC_WARN_UNDECLARED_SELECTOR = YES; 316 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 317 | GCC_WARN_UNUSED_FUNCTION = YES; 318 | GCC_WARN_UNUSED_VARIABLE = YES; 319 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 320 | MTL_ENABLE_DEBUG_INFO = NO; 321 | SDKROOT = iphoneos; 322 | VALIDATE_PRODUCT = YES; 323 | }; 324 | name = Release; 325 | }; 326 | 56B1A7EA1A8A4241008C5470 /* Debug */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 330 | FRAMEWORK_SEARCH_PATHS = ( 331 | "$(inherited)", 332 | "$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/HCSStarRatingViewSample-ejkjubavkfbjqpgdhbakhyojojhb/Build/Products/Debug-iphoneos", 333 | ); 334 | INFOPLIST_FILE = HCSStarRatingViewSample/Info.plist; 335 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 336 | PRODUCT_NAME = "$(TARGET_NAME)"; 337 | }; 338 | name = Debug; 339 | }; 340 | 56B1A7EB1A8A4241008C5470 /* Release */ = { 341 | isa = XCBuildConfiguration; 342 | buildSettings = { 343 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 344 | FRAMEWORK_SEARCH_PATHS = ( 345 | "$(inherited)", 346 | "$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/HCSStarRatingViewSample-ejkjubavkfbjqpgdhbakhyojojhb/Build/Products/Debug-iphoneos", 347 | ); 348 | INFOPLIST_FILE = HCSStarRatingViewSample/Info.plist; 349 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 350 | PRODUCT_NAME = "$(TARGET_NAME)"; 351 | }; 352 | name = Release; 353 | }; 354 | /* End XCBuildConfiguration section */ 355 | 356 | /* Begin XCConfigurationList section */ 357 | 56B1A7C11A8A4240008C5470 /* Build configuration list for PBXProject "HCSStarRatingViewSample" */ = { 358 | isa = XCConfigurationList; 359 | buildConfigurations = ( 360 | 56B1A7E71A8A4241008C5470 /* Debug */, 361 | 56B1A7E81A8A4241008C5470 /* Release */, 362 | ); 363 | defaultConfigurationIsVisible = 0; 364 | defaultConfigurationName = Release; 365 | }; 366 | 56B1A7E91A8A4241008C5470 /* Build configuration list for PBXNativeTarget "HCSStarRatingViewSample" */ = { 367 | isa = XCConfigurationList; 368 | buildConfigurations = ( 369 | 56B1A7EA1A8A4241008C5470 /* Debug */, 370 | 56B1A7EB1A8A4241008C5470 /* Release */, 371 | ); 372 | defaultConfigurationIsVisible = 0; 373 | defaultConfigurationName = Release; 374 | }; 375 | /* End XCConfigurationList section */ 376 | }; 377 | rootObject = 56B1A7BE1A8A4240008C5470 /* Project object */; 378 | } 379 | -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // HCSStarRatingViewSample 4 | // 5 | // Created by Hugo Sousa on 10/02/15. 6 | // Copyright (c) 2015 Hugo Sousa. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // HCSStarRatingViewSample 4 | // 5 | // Created by Hugo Sousa on 10/02/15. 6 | // Copyright (c) 2015 Hugo Sousa. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 97 | 104 | 111 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-empty.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "heart-empty.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "heart-empty@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "heart-empty@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-empty.imageset/heart-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Sample/HCSStarRatingViewSample/Images.xcassets/heart-empty.imageset/heart-empty.png -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-empty.imageset/heart-empty@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Sample/HCSStarRatingViewSample/Images.xcassets/heart-empty.imageset/heart-empty@2x.png -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-empty.imageset/heart-empty@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Sample/HCSStarRatingViewSample/Images.xcassets/heart-empty.imageset/heart-empty@3x.png -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-full.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "heart-full.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "heart-full@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "heart-full@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-full.imageset/heart-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Sample/HCSStarRatingViewSample/Images.xcassets/heart-full.imageset/heart-full.png -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-full.imageset/heart-full@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Sample/HCSStarRatingViewSample/Images.xcassets/heart-full.imageset/heart-full@2x.png -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-full.imageset/heart-full@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Sample/HCSStarRatingViewSample/Images.xcassets/heart-full.imageset/heart-full@3x.png -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-half.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "heart-half.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "heart-half@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "heart-half@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-half.imageset/heart-half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Sample/HCSStarRatingViewSample/Images.xcassets/heart-half.imageset/heart-half.png -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-half.imageset/heart-half@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Sample/HCSStarRatingViewSample/Images.xcassets/heart-half.imageset/heart-half@2x.png -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Images.xcassets/heart-half.imageset/heart-half@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsousa/HCSStarRatingView/c8d368a473fe7501199041d40c406692d921c4aa/Sample/HCSStarRatingViewSample/Images.xcassets/heart-half.imageset/heart-half@3x.png -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.hsousa.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // HCSStarRatingViewSample 4 | // 5 | // Created by Hugo Sousa on 10/02/15. 6 | // Copyright (c) 2015 Hugo Sousa. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // HCSStarRatingViewSample 4 | // 5 | // Created by Hugo Sousa on 10/02/15. 6 | // Copyright (c) 2015 Hugo Sousa. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import 11 | 12 | @interface ViewController () 13 | @property (nonatomic, weak) IBOutlet UILabel *lastRatingTitleLabel; 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | HCSStarRatingView *starRatingView = [HCSStarRatingView new]; 22 | starRatingView.maximumValue = 10; 23 | starRatingView.minimumValue = 0; 24 | starRatingView.value = 4; 25 | starRatingView.tintColor = [UIColor redColor]; 26 | starRatingView.allowsHalfStars = YES; 27 | starRatingView.emptyStarImage = [[UIImage imageNamed:@"heart-empty"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 28 | starRatingView.filledStarImage = [[UIImage imageNamed:@"heart-full"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 29 | [starRatingView addTarget:self action:@selector(didChangeValue:) forControlEvents:UIControlEventValueChanged]; 30 | [self.view addSubview:starRatingView]; 31 | 32 | // auto layout 33 | starRatingView.translatesAutoresizingMaskIntoConstraints = NO; 34 | [[NSLayoutConstraint constraintWithItem:starRatingView 35 | attribute:NSLayoutAttributeTop 36 | relatedBy:NSLayoutRelationEqual 37 | toItem:self.lastRatingTitleLabel 38 | attribute:NSLayoutAttributeBottom 39 | multiplier:1.f 40 | constant:8.f] setActive:YES]; 41 | [[NSLayoutConstraint constraintWithItem:starRatingView 42 | attribute:NSLayoutAttributeCenterX 43 | relatedBy:NSLayoutRelationEqual 44 | toItem:self.view 45 | attribute:NSLayoutAttributeCenterX 46 | multiplier:1.f 47 | constant:0.f] setActive:YES]; 48 | [[NSLayoutConstraint constraintWithItem:starRatingView 49 | attribute:NSLayoutAttributeWidth 50 | relatedBy:NSLayoutRelationLessThanOrEqual 51 | toItem:self.view 52 | attribute:NSLayoutAttributeWidth 53 | multiplier:.9f 54 | constant:0.f] setActive:YES]; 55 | } 56 | 57 | - (void)didReceiveMemoryWarning { 58 | [super didReceiveMemoryWarning]; 59 | // Dispose of any resources that can be recreated. 60 | } 61 | 62 | - (IBAction)didChangeValue:(HCSStarRatingView *)sender { 63 | NSLog(@"Changed rating to %.1f", sender.value); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Sample/HCSStarRatingViewSample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HCSStarRatingViewSample 4 | // 5 | // Created by Hugo Sousa on 10/02/15. 6 | // Copyright (c) 2015 Hugo Sousa. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------