diff --git a/Example/GrowingTextView.xcodeproj/project.pbxproj b/Example/GrowingTextView.xcodeproj/project.pbxproj index c72d685..ea92cc6 100644 --- a/Example/GrowingTextView.xcodeproj/project.pbxproj +++ b/Example/GrowingTextView.xcodeproj/project.pbxproj @@ -9,12 +9,13 @@ /* Begin PBXBuildFile section */ 0A41B0F6B7C779C0B7454A7F /* Pods_GrowingTextView_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E336CA14F020FFB7F36A401 /* Pods_GrowingTextView_Tests.framework */; }; 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; - 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; + 607FACD81AFB9204008FA782 /* Example1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* Example1.swift */; }; 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; A2C291FF1CD5335036A7799C /* Pods_GrowingTextView_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6901721E071F59387C8307F5 /* Pods_GrowingTextView_Example.framework */; }; + E8F6685B1E7A2374008DC918 /* Example2.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8F6685A1E7A2374008DC918 /* Example2.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -34,7 +35,7 @@ 607FACD01AFB9204008FA782 /* GrowingTextView_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GrowingTextView_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 607FACD71AFB9204008FA782 /* Example1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Example1.swift; sourceTree = ""; }; 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; @@ -47,6 +48,7 @@ 99E7D461BB194BB44BEA6391 /* Pods-GrowingTextView_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GrowingTextView_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GrowingTextView_Tests/Pods-GrowingTextView_Tests.debug.xcconfig"; sourceTree = ""; }; B0107F8A265D1912F5F00A58 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; E1CD2345DF65C90D2105BD90 /* GrowingTextView.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = GrowingTextView.podspec; path = ../GrowingTextView.podspec; sourceTree = ""; }; + E8F6685A1E7A2374008DC918 /* Example2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Example2.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -94,10 +96,11 @@ isa = PBXGroup; children = ( 607FACD51AFB9204008FA782 /* AppDelegate.swift */, - 607FACD71AFB9204008FA782 /* ViewController.swift */, + 607FACD71AFB9204008FA782 /* Example1.swift */, + E8F6685A1E7A2374008DC918 /* Example2.swift */, 607FACD91AFB9204008FA782 /* Main.storyboard */, - 607FACDC1AFB9204008FA782 /* Images.xcassets */, 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, + 607FACDC1AFB9204008FA782 /* Images.xcassets */, 607FACD31AFB9204008FA782 /* Supporting Files */, ); name = "Example for GrowingTextView"; @@ -210,7 +213,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0820; ORGANIZATIONNAME = CocoaPods; TargetAttributes = { 607FACCF1AFB9204008FA782 = { @@ -361,7 +364,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, + 607FACD81AFB9204008FA782 /* Example1.swift in Sources */, + E8F6685B1E7A2374008DC918 /* Example2.swift in Sources */, 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/GrowingTextView.xcodeproj/xcshareddata/xcschemes/GrowingTextView-Example.xcscheme b/Example/GrowingTextView.xcodeproj/xcshareddata/xcschemes/GrowingTextView-Example.xcscheme index 8e17c01..4ca9ace 100644 --- a/Example/GrowingTextView.xcodeproj/xcshareddata/xcschemes/GrowingTextView-Example.xcscheme +++ b/Example/GrowingTextView.xcodeproj/xcshareddata/xcschemes/GrowingTextView-Example.xcscheme @@ -1,6 +1,6 @@ - + + + + + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + + @@ -37,7 +182,25 @@ - + + + + + + + + + + + + + + + + + + + diff --git a/Example/GrowingTextView/ViewController.swift b/Example/GrowingTextView/Example1.swift similarity index 80% rename from Example/GrowingTextView/ViewController.swift rename to Example/GrowingTextView/Example1.swift index 41203a5..8a7b31a 100644 --- a/Example/GrowingTextView/ViewController.swift +++ b/Example/GrowingTextView/Example1.swift @@ -9,14 +9,15 @@ import UIKit import GrowingTextView -class ViewController: UIViewController { +class Example1: UIViewController { - @IBOutlet weak var bottomConstraint: NSLayoutConstraint! @IBOutlet weak var inputToolbar: UIToolbar! + @IBOutlet weak var bottomConstraint: NSLayoutConstraint! //*** Bottom Constraint of toolbar *** override func viewDidLoad() { super.viewDidLoad() + // *** Create GrowingTextView Instance *** let textView = GrowingTextView() textView.delegate = self textView.layer.cornerRadius = 4.0 @@ -28,8 +29,10 @@ class ViewController: UIViewController { textView.placeHolderLeftMargin = 5.0 textView.font = UIFont.systemFont(ofSize: 15) + // *** Add GrowingTextView into Toolbar inputToolbar.addSubview(textView) + // *** Set Autolayout constraints *** textView.translatesAutoresizingMaskIntoConstraints = false inputToolbar.translatesAutoresizingMaskIntoConstraints = false @@ -40,11 +43,10 @@ class ViewController: UIViewController { inputToolbar.addConstraints(vConstraints) self.view.layoutIfNeeded() -// constrain(inputToolbar, textView) { inputToolbar, textView in -// textView.edges == inset(inputToolbar.edges, 8, 8, 8, 8) -// } - + // *** Listen for keyboard show / hide *** NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) + + // *** Hide keyboard when tapping outside *** let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapGestureHandler)) view.addGestureRecognizer(tapGesture) } @@ -60,14 +62,17 @@ class ViewController: UIViewController { } func tapGestureHandler() { - inputToolbar.endEditing(true) + view.endEditing(true) } } -extension ViewController: GrowingTextViewDelegate { +extension Example1: GrowingTextViewDelegate { + + // *** Call layoutIfNeeded on superview for animation when changing height *** + func textViewDidChangeHeight(_ textView: GrowingTextView, height: CGFloat) { UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [.curveLinear], animations: { () -> Void in - self.inputToolbar.layoutIfNeeded() + self.view.layoutIfNeeded() }, completion: nil) } } diff --git a/Example/GrowingTextView/Example2.swift b/Example/GrowingTextView/Example2.swift new file mode 100644 index 0000000..5bd48ff --- /dev/null +++ b/Example/GrowingTextView/Example2.swift @@ -0,0 +1,64 @@ +// +// Example2.swift +// GrowingTextView +// +// Created by Tsang Kenneth on 16/3/2017. +// Copyright © 2017 CocoaPods. All rights reserved. +// + +import UIKit +import GrowingTextView + +class Example2: UIViewController { + + @IBOutlet weak var inputToolbar: UIView! + @IBOutlet weak var bottomConstraint: NSLayoutConstraint! //*** Bottom Constraint of toolbar *** + @IBOutlet weak var textView: GrowingTextView! + + override func viewDidLoad() { + super.viewDidLoad() + + // *** Set below parameters in Storyboard *** + // textView.delegate = self + // textView.layer.cornerRadius = 4.0 + // textView.maxLength = 200 + // textView.maxHeight = 70 + // textView.trimWhiteSpaceWhenEndEditing = true + // textView.placeHolder = "Say something..." + // textView.placeHolderColor = UIColor(white: 0.8, alpha: 1.0) + // textView.placeHolderLeftMargin = 5.0 + // textView.font = UIFont.systemFont(ofSize: 15) + + // *** Listen for keyboard show / hide *** + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) + + // *** Hide keyboard when tapping outside *** + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapGestureHandler)) + view.addGestureRecognizer(tapGesture) + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + func keyboardWillChangeFrame(_ notification: Notification) { + let endFrame = ((notification as NSNotification).userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue + bottomConstraint.constant = UIScreen.main.bounds.height - endFrame.origin.y + self.view.layoutIfNeeded() + } + + func tapGestureHandler() { + view.endEditing(true) + } +} + +extension Example2: GrowingTextViewDelegate { + + // *** Call layoutIfNeeded on superview for animation when changing height *** + + func textViewDidChangeHeight(_ textView: GrowingTextView, height: CGFloat) { + UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [.curveLinear], animations: { () -> Void in + self.view.layoutIfNeeded() + }, completion: nil) + } +} diff --git a/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/GrowingTextView.xcscheme b/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/GrowingTextView.xcscheme index 2d336b4..af8d33e 100644 --- a/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/GrowingTextView.xcscheme +++ b/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/GrowingTextView.xcscheme @@ -1,6 +1,6 @@ pod "GrowingTextView" ``` -Swift 2.3
+Swift 2.3 (Stopped update since Sep 2016)
+ ```ruby pod 'GrowingTextView', :git => 'https://github.com/KennethTsang/GrowingTextView.git', :branch => 'swift2' ``` @@ -34,21 +35,42 @@ Copy `GrowingTextView.swift` into your project. ## Usage +**Programmatically**
+ ```swift let textView = GrowingTextView() +textView.delegate = self addSubview(textView) ``` +**Storyboard**
+ +1. Drag a TextView into Storyboard. +2. Set class to "GrowingTextView". +3. Set delegate to it's view controller. + + +##### automaticallyAdjustsScrollViewInsets
+ +Sometime the view controller may incorrectly adjust the inset of textview automatically. To avoid this, set `automaticallyAdjustsScrollViewInsets` to `false` + +```swift +override func viewDidLoad() { + super.viewDidLoad() + automaticallyAdjustsScrollViewInsets = false +} +``` + ## Customization -Parameter | Type | Description | Default ---- | --- | --- | --- -*maxLength* | Int | Maximum text length. Exceeded text will be trimmed. 0 means no limit. | *0* -*trimWhiteSpaceWhenEndEditing* | Bool | Trim white space and new line characters when textview did end editing. | *true* -*placeHolder* | String? | PlaceHolder text. | *nil* -*placeHolderColor* | UIColor? | PlaceHolder text color. | *nil* -*placeHolderLeftMargin* | CGFloat | Left margin of PlaceHolder text. | *5.0* -*maxHeight* | CGFloat | Maximum height of textview. | *0.0* +| Parameter | Type | Description | Default | +| ------------------------------ | ------- | ---------------------------------------- | ------------------------------- | +| *maxLength* | Int | Maximum text length. Exceeded text will be trimmed. 0 means no limit. | *0* | +| *trimWhiteSpaceWhenEndEditing* | Bool | Trim white space and new line characters when textview did end editing. | *true* | +| *placeHolder* | String? | PlaceHolder text. | *nil* | +| *placeHolderColor* | UIColor | PlaceHolder text color. | UIColor(white: 0.8, alpha: 1.0) | +| *placeHolderLeftMargin* | CGFloat | Left margin of PlaceHolder text. | *5.0* | +| *maxHeight* | CGFloat | Maximum height of textview. | *0.0* | #### Examples @@ -62,19 +84,27 @@ textView.backgroundColor = UIColor.whiteColor() textView.layer.cornerRadius = 4.0 ``` -## Delegate +## Animation -Adopt `GrowingTextViewDelegate` instead of UITextViewDelegate, and listen to height change. +1. Adopt `GrowingTextViewDelegate` instead of UITextViewDelegate. +2. Implement textViewDidChangeHeight. +3. Call layoutIfNeeded() on superview inside the animation. ```swift class ViewController: UIViewController, GrowingTextViewDelegate { func textViewDidChangeHeight(_ textView: GrowingTextView, height: CGFloat) { - ... + UIView.animate(withDuration: 0.2) { + self.view.layoutIfNeeded() + } } } ``` -`GrowingTextViewDelegate` is inherited from UITextViewDelegate. For example: + + +## Delegate + +`GrowingTextViewDelegate` is inherited from UITextViewDelegate. You may use it's delegate function as a normal UITextView. ```swift class ViewController: UIViewController, GrowingTextViewDelegate { @@ -89,32 +119,6 @@ class ViewController: UIViewController, GrowingTextViewDelegate { Check out UITextViewDelegate here: [https://developer.apple.com/reference/uikit/uitextviewdelegate](https://developer.apple.com/reference/uikit/uitextviewdelegate) -## Animation - -To animate the height change, adopt `GrowingTextViewDelegate` instead of UITextViewDelegate. Implement textViewDidChangeHeight. Call layoutIfNeeded() inside an animation. - -```swift -class ViewController: UIViewController, GrowingTextViewDelegate { - func textViewDidChangeHeight(_ textView: GrowingTextView, height: CGFloat) { - UIView.animate(withDuration: 0.2) { - self.textView.layoutIfNeeded() - } - } -} -``` - -In some cases, you may also need to animate it's superview, e.g. toolbar. - -```swift -class ViewController: UIViewController, GrowingTextViewDelegate { - func textViewDidChangeHeight(_ textView: GrowingTextView, height: CGFloat) { - UIView.animate(withDuration: 0.2) { - self.myToolbar.layoutIfNeeded() - } - } -} -``` - ## Author Kenneth Tsang, kenneth.tsang@me.com