I am trying to accomplish an Idea I got in my head but I got stuck..
I need a TextView that expands in both ways: widht-height.
That has a minimum and maximum width, and minimum height.
That is centered in the middle of the parent (SCROLL) view.
And that has a button send at the bottom trailing part of the view.
Here's the idea:
So if the user types in the box then it expands in both directions. But there's a maximum width for it (so it doesn't go offscreen) but the height is not limited: due to the parent scrollview.
The problem is that the textView's height doesn't expand when text is breaking into new line.
Code:
func textViewDidChange(_ textView: UITextView) {
self.adjustTextViewFrames(textView: textView)
}
func adjustTextViewFrames(textView : UITextView){
var newSize = textView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
if newSize.width > self.view.bounds.width - 20 {
newSize.width = self.view.bounds.width - (self.view.bounds.width/10)
}
messageBubbleTextViewWidthConstraint.constant = newSize.width
messageBubbleTextViewHeightConstraint.constant = newSize.height
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
Try This :
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// let's create our text view
let textView = UITextView()
textView.frame = CGRect(x: 0, y: 0, width: 200, height: 100)
textView.backgroundColor = .lightGray
textView.text = "Here is some default text that we want to show and it might be a couple of lines that are word wrapped"
view.addSubview(textView)
// use auto layout to set my textview frame...kinda
textView.translatesAutoresizingMaskIntoConstraints = false
[
textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
textView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
textView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
textView.heightAnchor.constraint(equalToConstant: 50)
].forEach{ $0.isActive = true }
textView.font = UIFont.preferredFont(forTextStyle: .headline)
textView.delegate = self
textView.isScrollEnabled = false
textViewDidChange(textView)
}
}
extension ViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
print(textView.text)
let size = CGSize(width: view.frame.width, height: .infinity)
let estimatedSize = textView.sizeThatFits(size)
textView.constraints.forEach { (constraint) in
if constraint.firstAttribute == .height {
constraint.constant = estimatedSize.height
}
}
}
}
Credit goes to Brian Voong from Let's Build That App here link
Related
I have been unable to figure out how to solve this issue of mine. I tried to follow the answer from this post. How to change UIView height based on elements inside it
Like the post answer says to do, I have:
set autolayout constraints between UIContainerView top to the UITextView top and UIContainerView bottom to the UITextView bottom (#1)
set height constraint on the text view (#2) and change its constant when resizing the text view (#3)
I have to do this all programmatically. I first set the frame for the container view and give it a specified height. I'm not sure if that is okay too. I also add (#1) in viewDidLoad and am unsure if that's correct.
The text view is not able to increase height either with the current constraints (it is able to if I remove the topAnchor constraint but the container view still doesn't change size).
class ChatController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
lazy var containerView: UIView = {
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height * 0.075)
return containerView
}()
lazy var textView: UITextView = {
let textView = UITextView()
textView.text = "Enter message..."
textView.isScrollEnabled = false
textView.translatesAutoresizingMaskIntoConstraints = false
textView.delegate = self
return textView
}()
override func viewDidLoad() {
super.viewDidLoad()
...
textViewDidChange(self.textView)
addContainerSubViews()
(#1)
containerView.topAnchor.constraint(equalTo: self.textView.topAnchor, constant: -UIScreen.main.bounds.size.height * 0.075 * 0.2).isActive = true
containerView.bottomAnchor.constraint(equalTo: self.textView.bottomAnchor, constant: UIScreen.main.bounds.size.height * 0.075 * 0.2).isActive = true
}
func addContainerSubViews() {
let height = UIScreen.main.bounds.size.height
let width = UIScreen.main.bounds.size.width
let containerHeight = height * 0.075
...//constraints for imageView and sendButton...
containerView.addSubview(self.textView)
self.textView.leftAnchor.constraint(equalTo: imageView.rightAnchor, constant: width/20).isActive = true
self.textView.rightAnchor.constraint(equalTo: sendButton.leftAnchor, constant: -width/20).isActive = true
(#2)
self.textView.heightAnchor.constraint(equalToConstant: containerHeight * 0.6).isActive = true
}
override var inputAccessoryView: UIView? {
get {
return containerView
}
}
(#3)
func textViewDidChange(_ textView: UITextView) {
let size = CGSize(width: view.frame.width, height: .infinity)
let estimatedSize = textView.sizeThatFits(size)
textView.constraints.forEach { (constraint) in
if constraint.firstAttribute == .height {
constraint.constant = estimatedSize.height
}
}
}
You can do this all with auto-layout / constraints. Because a UITextView with scrolling disabled will "auto-size" its height based on the text, no need to calculate height and change constraint constant.
Here's an example -- it's from a previous answer, modified to include your image view and send button:
class ViewController: UIViewController {
let testLabel: InputLabel = InputLabel()
override func viewDidLoad() {
super.viewDidLoad()
let instructionLabel = UILabel()
instructionLabel.textAlignment = .center
instructionLabel.text = "Tap yellow label to edit..."
let centeringFrameView = UIView()
// label properties
let fnt: UIFont = .systemFont(ofSize: 32.0)
testLabel.isUserInteractionEnabled = true
testLabel.font = fnt
testLabel.adjustsFontSizeToFitWidth = true
testLabel.minimumScaleFactor = 0.25
testLabel.numberOfLines = 2
testLabel.setContentHuggingPriority(.required, for: .vertical)
let minLabelHeight = ceil(fnt.lineHeight)
// so we can see the frames
centeringFrameView.backgroundColor = .red
testLabel.backgroundColor = .yellow
[centeringFrameView, instructionLabel, testLabel].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
}
view.addSubview(instructionLabel)
view.addSubview(centeringFrameView)
centeringFrameView.addSubview(testLabel)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// instruction label centered at top
instructionLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
instructionLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
// centeringFrameView 20-pts from instructionLabel bottom
centeringFrameView.topAnchor.constraint(equalTo: instructionLabel.bottomAnchor, constant: 20.0),
// Leading / Trailing with 20-pts "padding"
centeringFrameView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
centeringFrameView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// test label centered vertically in centeringFrameView
testLabel.centerYAnchor.constraint(equalTo: centeringFrameView.centerYAnchor, constant: 0.0),
// Leading / Trailing with 20-pts "padding"
testLabel.leadingAnchor.constraint(equalTo: centeringFrameView.leadingAnchor, constant: 20.0),
testLabel.trailingAnchor.constraint(equalTo: centeringFrameView.trailingAnchor, constant: -20.0),
// height will be zero if label has no text,
// so give it a min height of one line
testLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: minLabelHeight),
// centeringFrameView height = 3 * minLabelHeight
centeringFrameView.heightAnchor.constraint(equalToConstant: minLabelHeight * 3.0)
])
// to handle user input
testLabel.editCallBack = { [weak self] str in
guard let self = self else { return }
self.testLabel.text = str
}
testLabel.doneCallBack = { [weak self] in
guard let self = self else { return }
// do something when user taps done / enter
}
let t = UITapGestureRecognizer(target: self, action: #selector(self.labelTapped(_:)))
testLabel.addGestureRecognizer(t)
}
#objc func labelTapped(_ g: UITapGestureRecognizer) -> Void {
testLabel.becomeFirstResponder()
testLabel.inputContainerView.theTextView.text = testLabel.text
testLabel.inputContainerView.theTextView.becomeFirstResponder()
}
}
class InputLabel: UILabel {
var editCallBack: ((String) -> ())?
var doneCallBack: (() -> ())?
override var canBecomeFirstResponder: Bool {
return true
}
override var canResignFirstResponder: Bool {
return true
}
override var inputAccessoryView: UIView? {
get { return inputContainerView }
}
lazy var inputContainerView: CustomInputAccessoryView = {
let v = CustomInputAccessoryView()
v.editCallBack = { [weak self] str in
guard let self = self else { return }
self.editCallBack?(str)
}
v.doneCallBack = { [weak self] in
guard let self = self else { return }
self.resignFirstResponder()
}
return v
}()
}
class CustomInputAccessoryView: UIView, UITextViewDelegate {
var editCallBack: ((String) -> ())?
var doneCallBack: (() -> ())?
let theTextView: UITextView = {
let tv = UITextView()
tv.isScrollEnabled = false
tv.font = .systemFont(ofSize: 16)
tv.autocorrectionType = .no
tv.returnKeyType = .done
return tv
}()
let imgView: UIImageView = {
let v = UIImageView()
v.contentMode = .scaleAspectFit
v.clipsToBounds = true
return v
}()
let sendButton: UIButton = {
let v = UIButton()
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .lightGray
autoresizingMask = [.flexibleHeight, .flexibleWidth]
if let img = UIImage(named: "testImage") {
imgView.image = img
} else {
imgView.backgroundColor = .systemBlue
}
let largeConfig = UIImage.SymbolConfiguration(pointSize: 22, weight: .regular, scale: .large)
let buttonImg = UIImage(systemName: "paperplane.fill", withConfiguration: largeConfig)
sendButton.setImage(buttonImg, for: .normal)
[theTextView, imgView, sendButton].forEach { v in
addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
}
// if we want to see the image view and button frames
//[imgView, sendButton].forEach { v in
// v.backgroundColor = .systemYellow
//}
NSLayoutConstraint.activate([
// constrain image view 40x40 with 8-pts leading
imgView.widthAnchor.constraint(equalToConstant: 40.0),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor),
imgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
// constrain image view 40x40 with 8-pts trailing
sendButton.widthAnchor.constraint(equalToConstant: 40.0),
sendButton.heightAnchor.constraint(equalTo: sendButton.widthAnchor),
sendButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
// constrain text view with 10-pts from
// image view trailing
// send button leading
theTextView.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 10),
theTextView.trailingAnchor.constraint(equalTo: sendButton.leadingAnchor, constant: -10),
// constrain image view and button
// centered vertically
// at least 8-pts top and bottom
imgView.centerYAnchor.constraint(equalTo: centerYAnchor),
sendButton.centerYAnchor.constraint(equalTo: centerYAnchor),
imgView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 8.0),
sendButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 8.0),
imgView.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -8.0),
sendButton.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -8.0),
// constrain text view 8-pts top/bottom
theTextView.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
theTextView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
])
theTextView.delegate = self
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
textView.resignFirstResponder()
doneCallBack?()
}
return true
}
func textViewDidChange(_ textView: UITextView) {
editCallBack?(textView.text ?? "")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
return .zero
}
}
Output:
I have been struggling to set create this, I have read numerous posts on here, but do not understand what I am doing wrong.
I have a UITextView.
I would like it to expand in height, up to a number and then fix at that height with scrolling enabled.
I have set my UITextFieldDelegateand added the following -
func textViewDidChange(_ textView: UITextView) {
let newSize = textView.sizeThatFits(CGSize(width: 0, height: CGFloat.greatestFiniteMagnitude))
textView.frame.size = CGSize(width: 0, height: min(100, newSize.height))
}
Doing this allow the to keep growing.
I'd now like it to stop and scroll instead at for example, a height of 100.
func textViewDidChange(_ textView: UITextView) {
let newSize = textView.sizeThatFits(CGSize(width: 0, height: CGFloat.greatestFiniteMagnitude))
let newHeight = min(100, newSize.height)
textView.frame.size = CGSize(width: 0, height: newHeight)
textView.isScrollEnabled = newHeight >= 100
}
All that happens at this point is the scrollview shrinks when isScrollEnabled is set as true.
My textView is anchored only to top, leading and trailing of it's parent.
How can I force it to not collapse once scroll is enabled?
EDIT
I have added the following which works, however the text seems to 'wobble' when entering anything at the fixed height state
func textViewDidChange(_ textView: UITextView) {
let newSize = textView.sizeThatFits(CGSize(width: 0, height: CGFloat.greatestFiniteMagnitude))
let newHeight = min(100, newSize.height)
textView.frame.size = CGSize(width: 0, height: newHeight)
if newHeight >= 100 {
textView.isScrollEnabled = true
textView.heightAnchor.constraint(equalToConstant: 100).isActive = true
} else {
textView.isScrollEnabled = false
textView.heightAnchor.constraint(equalToConstant: 100).isActive = false
}
}
It's better to make a default height constraint and connect it's outlet and play with it's constant
self.txH.constant = newHeight
self.view.layoutIfNeeded()
I am using UIStackView and adding three buttons to it. I want it so that the button with the most text (B1) will be auto resized to fit the width and the other buttons will share the same font size as B1.
#IBOutlet weak var stackView: UIStackView!
var btnTitles = [String]()
btnTitles.append("Practice Exams")
btnTitles.append("Test Taking Tips")
btnTitles.append("About")
createButtons(buttonTitles: btnTitles)
var min = CGFloat(Int.max) // keep track of min font
func createButtons(buttonTitles: [String]) {
var Buttons = [UIButton]()
for title in buttonTitles {
let button = makeButtonWithText(text: title)
// set the font to dynamically size
button.titleLabel!.numberOfLines = 1
button.titleLabel!.adjustsFontSizeToFitWidth = true
button.titleLabel!.baselineAdjustment = .alignCenters // I think it keeps it centered vertically
button.contentEdgeInsets = UIEdgeInsetsMake(5, 10, 5, 10); // set margins
if (button.titleLabel?.font.pointSize)! < min {
min = (button.titleLabel?.font.pointSize)! // to get the minimum font size of any of the buttons
}
stackView.addArrangedSubview(button)
Buttons.append(button)
}
}
func makeButtonWithText(text:String) -> UIButton {
var myButton = UIButton(type: UIButtonType.system)
//Set a frame for the button. Ignored in AutoLayout/ Stack Views
myButton.frame = CGRect(x: 30, y: 30, width: 150, height: 100)
// background color - light blue
myButton.backgroundColor = UIColor(red: 0.255, green: 0.561, blue: 0.847, alpha: 1)
//State dependent properties title and title color
myButton.setTitle(text, for: UIControlState.normal)
myButton.setTitleColor(UIColor.white, for: UIControlState.normal)
// set the font to dynamically size
myButton.titleLabel!.font = myButton.titleLabel!.font.withSize(70)
myButton.contentHorizontalAlignment = .center // align center
return myButton
}
I wanted to find the minimum font size and then set all the buttons to the minimum in viewDidAppear button the font prints as 70 for all of them even though they clearly appear different sizes (see image)
override func viewDidAppear(_ animated: Bool) {
print("viewDidAppear")
let button = stackView.arrangedSubviews[0] as! UIButton
print(button.titleLabel?.font.pointSize)
let button1 = stackView.arrangedSubviews[1] as! UIButton
print(button1.titleLabel?.font.pointSize)
let button2 = stackView.arrangedSubviews[2] as! UIButton
print(button2.titleLabel?.font.pointSize)
}
image
You can try playing around with this func to return the scaled-font-size of a label:
func actualFontSize(for aLabel: UILabel) -> CGFloat {
// label must have text, must have .minimumScaleFactor and must have .adjustsFontSizeToFitWidth == true
guard let str = aLabel.text,
aLabel.minimumScaleFactor > 0.0,
aLabel.adjustsFontSizeToFitWidth
else { return aLabel.font.pointSize }
let attributes = [NSAttributedString.Key.font : aLabel.font]
let attStr = NSMutableAttributedString(string:str, attributes:attributes as [NSAttributedString.Key : Any])
let context = NSStringDrawingContext()
context.minimumScaleFactor = aLabel.minimumScaleFactor
_ = attStr.boundingRect(with: aLabel.bounds.size, options: .usesLineFragmentOrigin, context: context)
return aLabel.font.pointSize * context.actualScaleFactor
}
On viewDidAppear() you would loop through the buttons, getting the smallest actual font size, then set the font size for each button to that value.
It will take some experimentation... For one thing, I've noticed in the past that font-sizes can get rounded - so setting a label's font point size to 20.123456789 won't necessarily give you that exact point size. Also, since this changes the actual font size assigned to the labels, you'll need to do some resetting if you change the button title dynamically. Probably also need to account for button frame changes (such as with device rotation, etc).
But... here is a quick test that you can run to see the approach:
class TestViewController: UIViewController {
let stackView: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .vertical
v.alignment = .center
v.distribution = .fillEqually
v.spacing = 8
return v
}()
var btnTitles = [String]()
var theButtons = [UIButton]()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
fixButtonFonts()
}
func setupUI() -> Void {
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40),
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40),
])
btnTitles.append("Practice Exams")
btnTitles.append("Test Taking Tips")
btnTitles.append("About")
createButtons(buttonTitles: btnTitles)
}
func fixButtonFonts() -> Void {
var minActual = CGFloat(70)
// get the smallest actual font size
theButtons.forEach { btn in
if let lbl = btn.titleLabel {
let act = actualFontSize(for: lbl)
// for debugging
//print("actual font size: \(act)")
minActual = Swift.min(minActual, act)
}
}
// set font size for each button
theButtons.forEach { btn in
if let lbl = btn.titleLabel {
lbl.font = lbl.font.withSize(minActual)
}
}
}
func createButtons(buttonTitles: [String]) {
for title in buttonTitles {
let button = makeButtonWithText(text: title)
// set the font to dynamically size
button.titleLabel!.numberOfLines = 1
button.titleLabel!.adjustsFontSizeToFitWidth = true
// .minimumScaleFactor is required
button.titleLabel!.minimumScaleFactor = 0.05
button.titleLabel!.baselineAdjustment = .alignCenters // I think it keeps it centered vertically
button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10); // set margins
stackView.addArrangedSubview(button)
theButtons.append(button)
}
}
func makeButtonWithText(text:String) -> UIButton {
let myButton = UIButton(type: UIButton.ButtonType.system)
//Set a frame for the button. Ignored in AutoLayout/ Stack Views
myButton.frame = CGRect(x: 30, y: 30, width: 150, height: 100)
// background color - light blue
myButton.backgroundColor = UIColor(red: 0.255, green: 0.561, blue: 0.847, alpha: 1)
//State dependent properties title and title color
myButton.setTitle(text, for: UIControl.State.normal)
myButton.setTitleColor(UIColor.white, for: UIControl.State.normal)
// set the font to dynamically size
myButton.titleLabel!.font = myButton.titleLabel!.font.withSize(70)
myButton.contentHorizontalAlignment = .center // align center
return myButton
}
func actualFontSize(for aLabel: UILabel) -> CGFloat {
// label must have text, must have .minimumScaleFactor and must have .adjustsFontSizeToFitWidth == true
guard let str = aLabel.text,
aLabel.minimumScaleFactor > 0.0,
aLabel.adjustsFontSizeToFitWidth
else { return aLabel.font.pointSize }
let attributes = [NSAttributedString.Key.font : aLabel.font]
let attStr = NSMutableAttributedString(string:str, attributes:attributes as [NSAttributedString.Key : Any])
let context = NSStringDrawingContext()
context.minimumScaleFactor = aLabel.minimumScaleFactor
_ = attStr.boundingRect(with: aLabel.bounds.size, options: .usesLineFragmentOrigin, context: context)
return aLabel.font.pointSize * context.actualScaleFactor
}
}
Result:
I have one method that shows a photo and zooms in it if the user wants. I used a scroll view so that the user could scroll if he zooms in a photo. My zoom works but it zoom into the top left corner and my scroll view doesn't scroll anywhere else even though it shows vertical and horizontal bars responsible for scroll moving.
class ImageViewController: UIViewController, UIScrollViewDelegate {
var myscrollView: UIScrollView!
var imgView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
myscrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
self.view.addSubview(myscrollView)
myscrollView.delegate = self
// myscrollView.scrollRectToVisible(CGRect(x: 100, y: 100, width: 1, height: 1), animated: true)
// myscrollView.setContentOffset(CGPoint(x: 0, y: view.frame.size.height), animated: true)
// myscrollView.contentSize = CGSize(width: view.frame.width, height: view.frame.height)
print("\(myscrollView.frame.height) \(myscrollView.frame.width)")
//myscrollView.contentSize = CGSize(width: 1000, height: 2000)
myscrollView.isScrollEnabled = true
myscrollView.minimumZoomScale = 1.0
myscrollView.maximumZoomScale = 10.0//maximum zoom scale you want
myscrollView.zoomScale = 1.0
myscrollView.alwaysBounceVertical = false
myscrollView.alwaysBounceHorizontal = false
myscrollView.showsVerticalScrollIndicator = true
myscrollView.flashScrollIndicators()
// scrollView.isPagingEnabled = false
// self.scrollView.contentSize = self.view.frame.size * 2//or what ever size you want to set
// myscrollView.contentOffset = CGPointMake((myscrollView.contentSize.width-myscrollView.frame.size.width)*.5f, .0f);//scroll to center
// myscrollView.contentOffset = CGPoint((myscrollView.contentSize.width-myscrollView.frame.size.width)*.5f, .0f)
myscrollView.addSubview(imgView)
//self.view.addSubview(imgView)
imgView!.layer.cornerRadius = 11.0
imgView.bounds = myscrollView.bounds
//imgView!.clipsToBounds = false
imgView.isUserInteractionEnabled = true
imgView.heightAnchor.constraint(equalToConstant: 50).isActive = true
// imgView.widthAnchor.constraint(equalToConstant: 50).isActive = true
imgView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 64).isActive = true
imgView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
imgView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
// imgView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
imgView.translatesAutoresizingMaskIntoConstraints = false
myscrollView.contentSize = CGSize(width: imgView.frame.width , height: 2000)
// myscrollView.contentSize = imgView.bounds.size
// Do any additional setup after loading the view.
}
// func scrollViewDidZoom(_ scrollView: UIScrollView) {
// var offsetY: CGFloat!
// offsetY = 0;
// if (scrollView.zoomScale > 1){
// offsetY = CGFloat(scrollView.contentOffset.y);
// }
// //CGPoint(x: scrollView.contentOffset.x, y: offsetY)
// scrollView.setContentOffset(CGPoint(x:
// scrollView.contentOffset.x, y: offsetY), animated: true)
//
// // [aScrollView setContentOffset:
// CGPointMake(aScrollView.contentOffset.x, offsetY)];
//
// }
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imgView
}
// override func viewDidLayoutSubviews() {
// myscrollView.delegate = self
// myscrollView.contentSize =
//CGSize(width:self.view.frame.size.width, height: 1000)
// }
// func viewForZooming(in scrollView: UIScrollView) -> UIView? {
// return imgView
// }
}
I tried to set contentSize manually to some big or small values and well as using frame.width and height but none of them seem to be working. Can someone please help what is my problem? Thanks in advance :)
Just try these lines of code and check, What are you missing-
let screenSize: CGRect = UIScreen.main.bounds
let scrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.contentSize = CGSize(width: screenSize.width, height: 1300)
scrollView.frame = CGRect(x: 0, y: 70, width: screenSize.width, height: screenSize.height-70)
scrollView.backgroundColor = UIColor.clear
view.addSubview(scrollView)
}
Try implementing scrollViewDidZoom method as follows;
func scrollViewDidZoom(_ scrollView: UIScrollView) {
let offsetX = max((scrollView.bounds.size.width-scrollView.contentSize.width) * 0.5, 0.0)
let offsetY = max((scrollView.bounds.size.height-scrollView.contentSize.height) * 0.5, 0.0)
self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0)
}
I'm resizing the view that a textview belongs to and the text shakes when the view either gets bigger or gets smaller.
Declaration of said text view:
lazy var textview: UITextView = {
let textView = UITextView()
textView.text = ""
textView.font = .systemFont(ofSize: 12, weight: UIFontWeightMedium)
textView.isScrollEnabled = false
textView.isEditable = false
textView.isSelectable = true
textView.isUserInteractionEnabled = true
textView.translatesAutoresizingMaskIntoConstraints = false
textView.textAlignment = .center
textView.textColor = .lightGray
textView.dataDetectorTypes = .link
return textView
}()
I'm resizing the view that it's in to fit the full screen like this
if let window = UIApplication.shared.keyWindow {
let statusBarHeight = UIApplication.shared.statusBarFrame.size.height
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveLinear, animations: {
self.frame = CGRect(x: 0, y: statusBarHeight, width: window.frame.width, height: window.frame.height - statusBarHeight)
self.layer.cornerRadius = 0
self.layoutIfNeeded()
}, completion: nil)
}
Upon doing so, the view expands perfectly but the textviews text does a bounce effect that makes the animation look extremely unprofessional... any advice?
Edit: It seems like when I remove the center text alignment option it works fine. How do I make it work with the text center aligned?
edit: I took another look at this and attempted to use the technique based in UIScrollView animation of height and contentOffset "jumps" content from bottom.
Here's a minimal working example with text view with centered text alignment which is working for me!
I'd recommend managing animations either to be all constraint based, or all frame based. I attempted a version where the animation is driven by updating the container view frame but it was starting to take too long to left it at this constraint based approach.
Hope this points you in the right direction :)
import UIKit
class ViewController: UIViewController {
lazy var textView: UITextView = {
let textView = UITextView()
textView.text = "testing text view"
textView.textAlignment = .center
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
lazy var containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var widthConstraint: NSLayoutConstraint!
var topAnchor: NSLayoutConstraint!
override func viewDidLoad() {
view.backgroundColor = .groupTableViewBackground
// add container view and constraints
view.addSubview(containerView)
containerView.frame = view.bounds.insetBy(dx: 100, dy: 200)
containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
containerView.heightAnchor.constraint(equalToConstant: 100).isActive = true
// keep reference to topAnchor and width as properties to animate
topAnchor = containerView.topAnchor.constraint(lessThanOrEqualTo: view.topAnchor, constant: 100)
widthConstraint = containerView.widthAnchor.constraint(equalToConstant: 300)
topAnchor.isActive = true
widthConstraint.isActive = true
// add text view to container view and set constraints
containerView.addSubview(textView)
textView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
textView.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true
textView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
textView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
}
#IBAction func toggleResize(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
view.layoutIfNeeded()
widthConstraint.constant = sender.isSelected ? view.bounds.width : 300
topAnchor.constant = sender.isSelected ? 20 : 100
// caculate the textView content offset for starting position based on
// expected end position at end of the animation
let xOffset = (textView.bounds.width - widthConstraint.constant) / 2
textView.contentOffset = CGPoint(x: -xOffset, y: textView.contentOffset.y)
UIView.animate(withDuration: 1) {
self.view.layoutIfNeeded()
}
}
}