I'm creating a comment section much like the one Facebook uses for it's messaging section in it's iOS app. I want the UITextView to resize the height so the text I'm typing fits inside it rather than you having to scroll to see the text that overflows. Any ideas how I might go about doing this? I've looked into maybe using a CGRect that is assigned to the size and height of the text view which then matches the content size:
CGRect textFrame = textView.frame;
textFrame.size.height = textView.contentSize.height;
textView.frame = textFrame;
I assume I need some sort of function that detects when the text reaches the bounds of the UITextView and then resizes the height of the view? Has anyone struggled with this same concept?
You can adjust frame in this delegate method, do not forget to set textView's delegate to self.
-(BOOL)textView:(UITextView *)_textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
[self adjustFrames];
return YES;
}
-(void) adjustFrames
{
CGRect textFrame = textView.frame;
textFrame.size.height = textView.contentSize.height;
textView.frame = textFrame;
}
this solution is for iOS6 and prior... for iOS7 refer to this
StackOverflow Answer
This is my solution, using autolayout and textView.contentSize.height. Tested on iOS8 Xcode6.3 beta4.
There's one catch about the setContentOffset at the end. I put it to avoid "wrong contentOffset" artefact when line count changes. It adds an extra unwanted blank space below the last line and it doesn't look very nice unless you set it back right after changing the constraint. Took me hours to figure this out!
// set this up somewhere
let minTextViewHeight: CGFloat = 32
let maxTextViewHeight: CGFloat = 64
func textViewDidChange(textView: UITextView) {
var height = ceil(textView.contentSize.height) // ceil to avoid decimal
if (height < minTextViewHeight + 5) { // min cap, + 5 to avoid tiny height difference at min height
height = minTextViewHeight
}
if (height > maxTextViewHeight) { // max cap
height = maxTextViewHeight
}
if height != textViewHeight.constant { // set when height changed
textViewHeight.constant = height // change the value of NSLayoutConstraint
textView.setContentOffset(CGPointZero, animated: false) // scroll to top to avoid "wrong contentOffset" artefact when line count changes
}
}
First set the minimum height constraints to your TextView:
textView.heightAnchor.constraint(greaterThanOrEqualTo: view.heightAnchor, multiplier: 0.20)
(Make sure you are setting greaterThanOrEqualTo Constraint so that if intrinsic content height is more than this height, it takes intrinsic content height )
OR simple constant
textView.heightAnchor.constraint(greaterThanOrEqualToConstant: someConstant)
While configuring your textView, set isScrollEnabled to false
textView.isScrollEnabled = false
Now when you type on the textView, its intrinsic content size height will increase and it will push views below it automatically.
On the TableViewController that holds the UITextView, update the data from the tableViewDataSource that gets put in the cell and then simply call this:
tableView.beginUpdates()
tableView.endUpdates()
Unlike tableView.reloadData(), this does not call resignFirstResponder
contentsize will not work in ios 7.
Try this:
CGFloat textViewContentHeight = textView.contentSize.height;
textViewContentHeight = ceilf([textView sizeThatFits:textView.frame.size].height + 9);
Related
I'm building a simple NSGridView, and want to have a custom NSView as each element of the grid. Eventually, each NSView will be a xib based label (NSTextField) centered in the NSView.
The problem I am having is with the intrinsic size of the NSView. I want to define the size of the NSView and have auto layout work based on that. I added this code to the custom view (labelView):
override var intrinsicContentSize: NSSize {
return NSSize(width:100, height:100);
};
And it is indeed called; but apparently ignored. As a test, I have on the same row some other labels, and the height for the row is always set to the largest of the row text heights (including the label in the custom view); but the length is set to the longest of column text fields, ignoring the label in the custom view. And anyway, I want to arbitrarily make the NSView a certain height and length, as I tried (but failed) to do with the intrinsicContentSize.
NSStackview seems to do the right thing; but NSGridView does not.
I can force the width of a particular column with
grid.column(at:0).width = 400;
but want I really want to do is define the size of the NSView, and let autolayout use that as a building block.
This strikes me as a conceptual error on my part, so if someone could explain these NSGridView-autolayout-NSView subtleties, I think many might benefit.
I was having the exact same issue, tried to use custom NSView's inside a NSGridView and couldn't get them to draw correctly. What finally worked for me was setting the following:
let gridSize = 5
let cellSize: CGFloat = 50
gridView.xPlacement = .fill // this was key part of the solution
gridView.yPlacement = .fill
for i in 0 ..< gridSize {
gridView.row(at: i).height = cellSize
gridView.column(at: i).width = cellSize
}
Note that I'm setting the size of each cell with the row height and column width of the NSGridView, and not using the NSView size, but this is the only way I got it working.
I am using AutoLayout. I have a view with %10 width and 200 fixed height. I placed a UILabel into that view with 0 margins. Then I am rotating the UILabel, changing the content and calling sizeToFit() function. At the load of the controller the rotation works but the content is being truncated. After I did a button click on the controller, the UILabel is being enlarged and content is being fit.
What should I do to avoid this problem?
Note: I checked the width and height of the label's frame with debugging. After sizeToFit() the width and height of the frame changes as expected. However, the UI is not refreshing. I also tried label.setNeedsLayout() and label.layoutIfNeeded() but nothing changed.
For rotation I am using below extension:
extension UILabel {
#IBInspectable
var rotation: Int {
get {
return 0
} set {
let radians = CGFloat(CGFloat(Double.pi) * CGFloat(newValue) / CGFloat(180.0))
self.transform = CGAffineTransform(rotationAngle: radians)
}
}
}
You can try view.layoutIfNeeded() instead of label.layoutIfNeeded()
I have a TextView as shown below
I am not able to scroll the text view , I have added UITextViewDelegate in UIViewController class as well as set isUserInteractionEnabled property in textViewDidBeginEditing
func textViewDidBeginEditing(_ textView: UITextView) {
textView.backgroundColor = UIColor.lightGray
textView.isEditable = false
textView.isUserInteractionEnabled = true
textView.isScrollEnabled = true
}
What did I need to do?
Also, the scrolling is enabled in attribute inspector
This issue has occurred because the actual UITextView's size was more than screen size as shown below
The real answer is that the UITextView needs its content frame height inferior to its content size height to be able to scroll.
In your case, the content frame height is equal to the content size, so it doesn't scroll.
You just have to set the leading left, trailing, top space and bottom space to the View, but first make sure the Text View is smaller than the actual View (parent).
It starts to get complicated when you have a UITextView on a UIView which is either a view on a UIScrollView or directly on a UIScrollview.
The best thing to do, is to give each of them their own actions when scrolling.
Firstly, make sure you have all of your delegates for EVERYTHING set.
Make sure your UITextViews are not user restricted for what they don't need to be.
What I have to do in one of my published apps is this👇
func scrollViewWillBeginDragging(_ scrollView: UIScrollView)
{
if scrollView == textView1
{
// Do something if you actually want to, or just let textView1 scroll as intended
}
else if scrollView == textView2
{
// Do something if you actually want to, or just let textView2 scroll as intended
}
else if scrollView == zoomingScroll
{
// Do something if you need to or leave it
}
else
{
// Do something with all the other scrollable views if you need to
}
}
I have an autolayouted UIView and need to know the width of it. How do I find the width the easiest way in Swift?
You can find the width of any view by
let width = yourView.bounds.width
If you have applied a width constraint to the view and you have an outlet for that constraint then you can find it by.
let width = yourWidthConstraint.constant
The right place to get the UIScreen frame data is in viewDidLayoutSubviews as it is called after the subViews have been laid out in screen also it is called after every time your device changes orientation such as your width will be different when your user goes into landscape mode.This is called after viewWillAppear:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let viewWidth = self.myView.bounds.width
}
Inside implementation your view you can use
override func layoutSubviews() {
super.layoutSubviews()
// you can get width of view in this function
print(self.frame.size.width)
}
I have this table view whose table view cells have the width and the height of the screen. Each showing an image.
While you scroll through the table view you can have one table view cell occupying the whole screen. Or at the most you can see 2 table view cells on the screen of the app at the same time.
My question is: is there any way I can find out which of the 2 table view cells occupies more screen height than the other?
Here is the easy way to find out which cell is visible more than 60%.
func scrollViewDidScroll(scrollView: UIScrollView) {
checkWhichVideoToEnable()
}
func checkWhichVideoToEnable() {
for cell in tableView.visibleCells as [UITableViewCell] {
if cell.isKindOfClass(UITableViewCell) {
let indexPath = tableView.indexPathForCell(cell)
let cellRect = tableView.rectForRowAtIndexPath(indexPath!)
let superView = tableView.superview
let convertedRect = tableView.convertRect(cellRect, toView: superView)
let intersect = CGRectIntersection(tableView.frame, convertedRect)
let visibleHeight = CGRectGetHeight(intersect)
if visibleHeight > self.view.bounds.size.height * 0.6 { // only if 60% of the cell is visible.
//cell is visible more than 60%
print(indexPath?.row) //your visible cell.
}
}
}
}
Reference from HERE.
Look at the second visible cell (tableView.visibleCells[1]) and check its y position. If it's less than half the height of the tableView, it's taking up more of the screen, else less. Break ties any way you want.
edit
The y position for a given indexPath is fixed, so you need to take into account the tableView's contentOffset.
CGFloat yOrigin = [tableView.visibleCells[1] frame].origin.y - tableView.contentOffset.y;
you could maybe try to get the tableView's visibleCells and check the origin of those cells. Since you'll have 2 visibleCells at any time, comparing their respective frame's y value could tell you which is occupying more height