diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ab9936b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,46 @@ +name: CI +on: + push: + branches: + - master + pull_request: + +jobs: + build: + runs-on: macos-latest + strategy: + matrix: + run-config: + - { xcode_version: '12.4', simulator: 'name=iPhone SE (2nd generation),OS=14.4' } + + steps: + - name: Checkout Project + uses: actions/checkout@v1 + + - name: Brew Update + run: brew update + + - name: Install Bundler + run: gem install bundler + + - name: Install Core Utils + run: if [ -z "$(brew ls --versions coreutils)" ] ; then brew install coreutils ; fi + + - name: Install XCPretty + run: gem install xcpretty --no-document --quiet + + - name: Show Xcode versions + run: ls -al /Applications/Xcode* + + - name: Select Xcode + run: sudo xcode-select -s /Applications/Xcode_${{ matrix.run-config['xcode_version'] }}.app + + - name: Current Xcode Selected + run: xcode-select -p + + - name: List Simulators + run: xcrun simctl list + + - name: Build + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "ExpandableLabelDemoSPM/ExpandableLabelDemoSPM.xcodeproj" -scheme "ExpandableLabelDemoSPM" -destination "${{ matrix.run-config['simulator'] }}" clean build | xcpretty + diff --git a/Classes/ExpandableLabel.swift b/Classes/ExpandableLabel.swift index 24463d3..25c726b 100644 --- a/Classes/ExpandableLabel.swift +++ b/Classes/ExpandableLabel.swift @@ -233,7 +233,8 @@ extension ExpandableLabel { let lineTextWithAddedLink = NSMutableAttributedString(attributedString: lineTextWithLastWordRemoved) if let ellipsis = self.ellipsis { lineTextWithAddedLink.append(ellipsis) - lineTextWithAddedLink.append(NSAttributedString(string: " ", attributes: [.font: self.font])) + lineTextWithAddedLink.append(NSAttributedString(string: " ", + attributes: [.font: (self.font ?? UIFont.preferredFont(forTextStyle: .body))])) } lineTextWithAddedLink.append(linkName) let fits = self.textFitsWidth(lineTextWithAddedLink) @@ -261,7 +262,8 @@ extension ExpandableLabel { let linkText = NSMutableAttributedString() if let ellipsis = self.ellipsis { linkText.append(ellipsis) - linkText.append(NSAttributedString(string: " ", attributes: [.font: self.font])) + linkText.append(NSAttributedString(string: " ", + attributes: [.font: (self.font ?? UIFont.preferredFont(forTextStyle: .body))])) } linkText.append(linkName) @@ -546,6 +548,8 @@ extension UILabel { return 1.0 case .left, .natural, .justified: return 0.0 + @unknown default: + fatalError() } } } diff --git a/ExpandableLabel.xcodeproj/project.pbxproj b/ExpandableLabel.xcodeproj/project.pbxproj index f18e94d..0e84cdf 100644 --- a/ExpandableLabel.xcodeproj/project.pbxproj +++ b/ExpandableLabel.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 1B0D8A441F3AAA50004142A4 /* ExpandableLabel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ExpandableLabel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1B0D8A501F3AAA6C004142A4 /* ExpandableLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpandableLabel.swift; sourceTree = ""; }; 1B0D8A511F3AAA6C004142A4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F685D23D232A593F0000133D /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -30,6 +31,7 @@ 1B0D8A3A1F3AAA50004142A4 = { isa = PBXGroup; children = ( + F685D23D232A593F0000133D /* Package.swift */, 1B0D8A4F1F3AAA6C004142A4 /* Classes */, 1B0D8A451F3AAA50004142A4 /* Products */, ); @@ -89,7 +91,7 @@ 1B0D8A3B1F3AAA50004142A4 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0900; + LastUpgradeCheck = 1240; TargetAttributes = { 1B0D8A431F3AAA50004142A4 = { CreatedOnToolsVersion = 8.3.3; @@ -100,10 +102,11 @@ }; buildConfigurationList = 1B0D8A3E1F3AAA50004142A4 /* Build configuration list for PBXProject "ExpandableLabel" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 1B0D8A3A1F3AAA50004142A4; productRefGroup = 1B0D8A451F3AAA50004142A4 /* Products */; @@ -151,6 +154,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -158,8 +162,10 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -211,6 +217,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -218,8 +225,10 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -264,7 +273,7 @@ PRODUCT_BUNDLE_IDENTIFIER = de.apploft.ExpandableLabel; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -282,7 +291,7 @@ PRODUCT_BUNDLE_IDENTIFIER = de.apploft.ExpandableLabel; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/ExpandableLabel.xcodeproj/xcshareddata/xcschemes/ExpandableLabel.xcscheme b/ExpandableLabel.xcodeproj/xcshareddata/xcschemes/ExpandableLabel.xcscheme index 90fca38..5c1c868 100644 --- a/ExpandableLabel.xcodeproj/xcshareddata/xcschemes/ExpandableLabel.xcscheme +++ b/ExpandableLabel.xcodeproj/xcshareddata/xcschemes/ExpandableLabel.xcscheme @@ -1,6 +1,6 @@ - - - - Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // 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. + // 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. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // 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. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // 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. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // 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. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Base.lproj/LaunchScreen.xib b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Base.lproj/LaunchScreen.xib new file mode 100644 index 0000000..4795ec0 --- /dev/null +++ b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Base.lproj/LaunchScreen.xib @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Base.lproj/Main.storyboard b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Base.lproj/Main.storyboard new file mode 100644 index 0000000..439627a --- /dev/null +++ b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Base.lproj/Main.storyboard @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/ExpandableCell.swift b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/ExpandableCell.swift new file mode 100644 index 0000000..804999c --- /dev/null +++ b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/ExpandableCell.swift @@ -0,0 +1,37 @@ +// +// ExpandableCell.swift +// +// Copyright (c) 2015 apploft. GmbH +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit +import ExpandableLabel + +class ExpandableCell : UITableViewCell { + + + @IBOutlet weak var expandableLabel: ExpandableLabel! + + override func prepareForReuse() { + super.prepareForReuse() + expandableLabel.collapsed = true + expandableLabel.text = nil + } +} \ No newline at end of file diff --git a/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Images.xcassets/AppIcon.appiconset/Contents.json b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..118c98f --- /dev/null +++ b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Info.plist b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Info.plist new file mode 100644 index 0000000..cb2b8b8 --- /dev/null +++ b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Label Demo + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/ViewController.swift b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/ViewController.swift new file mode 100644 index 0000000..d58afef --- /dev/null +++ b/ExpandableLabelDemoSPM/ExpandableLabelDemoSPM/ViewController.swift @@ -0,0 +1,169 @@ +// +// ViewController.swift +// +// Copyright (c) 2015 apploft. GmbH +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit +import ExpandableLabel + +class ViewController: UITableViewController, ExpandableLabelDelegate { + + let numberOfCells : NSInteger = 12 + var states : Array! + + override func viewDidLoad() { + super.viewDidLoad() + + states = [Bool](repeating: true, count: numberOfCells) + tableView.estimatedRowHeight = 44 + tableView.rowHeight = UITableView.automaticDimension + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + tableView.reloadData() + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let currentSource = preparedSources()[indexPath.row] + + let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ExpandableCell + cell.expandableLabel.delegate = self + + cell.expandableLabel.setLessLinkWith(lessLink: "Close", attributes: [.foregroundColor:UIColor.red], position: currentSource.textAlignment) + + cell.layoutIfNeeded() + + cell.expandableLabel.shouldCollapse = true + cell.expandableLabel.textReplacementType = currentSource.textReplacementType + cell.expandableLabel.numberOfLines = currentSource.numberOfLines + cell.expandableLabel.collapsed = states[indexPath.row] + cell.expandableLabel.text = currentSource.text + + return cell + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return states.count + } + + func preparedSources() -> [(text: String, textReplacementType: ExpandableLabel.TextReplacementType, numberOfLines: Int, textAlignment: NSTextAlignment)] { + return [(loremIpsumText(), .word, 3, .left), + (textWithNewLinesInCollapsedLine(), .word, 2, .center), + (textWithLongWordInCollapsedLine(), .character, 1, .right), + (textWithVeryLongWords(), .character, 1, .left), + (loremIpsumText(), .word, 4, .center), + (loremIpsumText(), .character, 3, .right), + (loremIpsumText(), .word, 2, .left), + (loremIpsumText(), .character, 5, .center), + (loremIpsumText(), .word, 3, .right), + (loremIpsumText(), .character, 1, .left), + (textWithShortWordsPerLine(), .character, 3, .center), + (textEmojis(), .character, 3, .left)] + } + + + func loremIpsumText() -> String { + return "On third line our text need be collapsed because we have ordinary text, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." + } + + func textWithNewLinesInCollapsedLine() -> String { + return "When u had new line specialChars \n More not appeared eirmod\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n tempor invidunt ut\n\n\n\n labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." + } + + func textWithLongWordInCollapsedLine() -> String { + return "When u had long word which not entered in one line More not appeared FooBaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaR tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." + } + + func textWithVeryLongWords() -> String { + return "FooBaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaR FooBaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaR FooBaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaR FooBaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaR Will show first line and will increase touch area for more voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." + } + + func textWithShortWordsPerLine() -> String { + return "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN" + } + + func textEmojis() -> String { + return "πŸ˜‚πŸ˜„πŸ˜ƒπŸ˜ŠπŸ˜πŸ˜—πŸ˜œπŸ˜…πŸ˜“β˜ΊοΈπŸ˜ΆπŸ€¦πŸ˜’πŸ˜πŸ˜ŸπŸ˜΅πŸ™πŸ€”πŸ€“β˜ΉοΈπŸ™„πŸ˜‘πŸ˜«πŸ˜±πŸ™‚πŸ˜§πŸ€΅πŸ˜ΆπŸ‘₯πŸ‘©β€β€οΈβ€πŸ‘©πŸ’–πŸ‘¨β€β€οΈβ€πŸ’‹β€πŸ‘¨πŸ’πŸ‘©β€πŸ‘©β€πŸ‘¦β€πŸ‘¦πŸ‘¦πŸ‘€πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦πŸ‘©β€β€οΈβ€πŸ‘©πŸ—¨πŸ•΄πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©πŸ‘§β˜ΉοΈπŸ˜ πŸ˜€πŸ˜†πŸ’šπŸ™„πŸ€’πŸ’‹πŸ˜ΏπŸ‘„" + } + + // + // MARK: ExpandableLabel Delegate + // + + func willExpandLabel(_ label: ExpandableLabel) { + tableView.beginUpdates() + } + + func didExpandLabel(_ label: ExpandableLabel) { + let point = label.convert(CGPoint.zero, to: tableView) + if let indexPath = tableView.indexPathForRow(at: point) as IndexPath? { + states[indexPath.row] = false + DispatchQueue.main.async { [weak self] in + self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true) + } + } + tableView.endUpdates() + } + + func willCollapseLabel(_ label: ExpandableLabel) { + tableView.beginUpdates() + } + + func didCollapseLabel(_ label: ExpandableLabel) { + let point = label.convert(CGPoint.zero, to: tableView) + if let indexPath = tableView.indexPathForRow(at: point) as IndexPath? { + states[indexPath.row] = true + DispatchQueue.main.async { [weak self] in + self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true) + } + } + tableView.endUpdates() + } +} + +extension String { + + func specialPriceAttributedStringWith(_ color: UIColor) -> NSMutableAttributedString { + let attributes = [NSAttributedString.Key.strikethroughStyle: NSNumber(value: NSUnderlineStyle.single.rawValue as Int), + .foregroundColor: color, .font: fontForPrice()] + return NSMutableAttributedString(attributedString: NSAttributedString(string: self, attributes: attributes)) + } + + func priceAttributedStringWith(_ color: UIColor) -> NSAttributedString { + let attributes = [NSAttributedString.Key.foregroundColor: color, .font: fontForPrice()] + + return NSAttributedString(string: self, attributes: attributes) + } + + func priceAttributedString(_ color: UIColor) -> NSAttributedString { + let attributes = [NSAttributedString.Key.foregroundColor: color] + + return NSAttributedString(string: self, attributes: attributes) + } + + fileprivate func fontForPrice() -> UIFont { + return UIFont(name: "Helvetica-Neue", size: 13) ?? UIFont() + } +} + + + diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..2adc4ed --- /dev/null +++ b/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version:5.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "ExpandableLabel", + products: [ + .library( + name: "ExpandableLabel", + targets: ["ExpandableLabel"]) + ], + dependencies: [ + ], + targets: [ + .target( + name: "ExpandableLabel", + dependencies: [], + path: "Classes") + ], + swiftLanguageVersions: [.v5] +) diff --git a/README.md b/README.md index 90a797a..9e088f2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,25 @@ github "apploft/ExpandableLabel" Run `carthage` to build the framework and drag the built `ExpandableLabel.framework` into your Xcode project. +### [Swift Package Manager] (Xcode 11+) + +[Swift Package Manager](https://swift.org/package-manager/) (SwiftPM) is a tool for managing the distribution of Swift code as well as C-family dependency. From Xcode 11, SwiftPM got natively integrated with Xcode. + +ExpandableLabel support SwiftPM from version > 0.5.2 . To use SwiftPM, you should use Xcode 11 to open your project. Click `File` -> `Swift Packages` -> `Add Package Dependency`, enter [ExpandableLabel repo's URL](https://github.com/apploft/ExpandableLabel/). Or you can login Xcode with your GitHub account and just type `ExpandableLabel` to search. + +After select the package, you can choose the dependency type (tagged version, branch or commit). Then Xcode will setup all the stuff for you. + +If you're a framework author and use ExpandableLabel as a dependency, update your `Package.swift` file: + +```swift +let package = Package( + dependencies: [ + .package(url: "https://github.com/apploft/ExpandableLabel.git") + ], + // ... +) +``` + # Usage Using ExpandableLabel is very simple. In your storyboard, set the custom class of your UILabel to ExpandableLabel and set the desired number of lines (for the collapsed state):