I want to make a UILabel's height expand depending on its text.
Here is what the view controller looks like, with the label selected:
Here is the code (I have tried a bunch of different similar things but this is what I have right now):
import UIKit
class ViewControllerTEST: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.frame = CGRectMake(0, 0, CGRectGetWidth(label.bounds), 0)
label.numberOfLines = 0
label.lineBreakMode = .ByWordWrapping
label.text = "This is a really\nlong string"
label.setNeedsLayout()
label.sizeToFit()
label.frame = CGRectMake(0, 0, CGRectGetWidth(label.bounds), CGRectGetHeight(label.bounds))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
And, as you can see here, it doesn't work as intended:
Don't use a frame, use autolayout. Add a top, leading, and trailing constraint to the label (I would suggest doing this in the storyboard). As long as you have lines equal to 0 (which you do), the height will adjust automatically. If you want to add the constraints in code, your viewDidLoad would look something like this:
override func viewDidLoad() {
super.viewDidLoad()
label.text = "This is a really\nlong string"
label.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(label)
let views = ["label": label]
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[label]-|", options: nil, metrics: nil, views: views))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[label]", options: nil, metrics: nil, views: views))
}
Related
I am looking for the text to be scroll all the way down to the bottom that is it. Just like you would see in movie credits. I do not want to move the x, y location of the textview just the text inside from top to bottom.
import UIKit
class ViewController: UIViewController {
#IBOutlet var text: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func play(_ sender: Any) {
}
}
Use a UIScrollView and scroll a UILabel with the text in it.
You can use standard UIView animation with timing and optionally animation curve and set the contentOffset inside the closure.
UIView.animate(withDuration: 10.0) { // 10 seconds
scrollView.contentOffset = CGPoint(x: 0, y: maxDesiredScrollPosition)
}
I have example project on GitHub you can see on ->
https://github.com/LetSwiftDev/ContainerViewInsideScroll/tree/master
I have two, view controller, one is normal view controller named oneViewController second is twoViewController inside have UIScrollView and when I clicked OPEN TWO button going to twoViewController inside ContainerView but;
My twoViewController have different height size like 1500 and inside have scrollview and don't scroll to bottom? I didn't see middle side, bottom side labels.
How can I fix it?
Any idea?
Also my centerViewController codes under below. I'm controlling to past view controllers with centerViewController
import UIKit
open class centerViewController: UIViewController {
fileprivate weak var viewController : UIViewController!
fileprivate var containerViewObjects = Dictionary<String,UIViewController>()
open var currentViewController : UIViewController{
get {
return self.viewController
}
}
fileprivate var segueIdentifier : String!
#IBInspectable internal var startUp : String!
override open func viewDidLoad() {
super.viewDidLoad()
}
open override func viewDidAppear(_ animated: Bool) {
if let identifier = startUp{
segueIdentifierReceivedFromParent(identifier)
}
}
override open func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func segueIdentifierReceivedFromParent(_ identifier: String){
self.segueIdentifier = identifier
self.performSegue(withIdentifier: self.segueIdentifier, sender: nil)
}
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueIdentifier{
if viewController != nil{
viewController.view.removeFromSuperview()
viewController = nil
}
if ((self.containerViewObjects[self.segueIdentifier] == nil)){
viewController = segue.destination
self.containerViewObjects[self.segueIdentifier] = viewController
}else{
for (key, value) in self.containerViewObjects{
if key == self.segueIdentifier{
viewController = value
}
}
}
self.addChildViewController(viewController)
viewController.view.frame = CGRect(x: 0,y: 0, width: self.view.frame.width,height: self.view.frame.height)
self.view.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
}
}
}
Here is your demo with solution. You can download it from here
https://www.dropbox.com/s/zvrjd0gp06xe5nw/ContainerViewInsideScroll-master%202.zip?dl=0
The thing is You have to learn autolayout for Scrollview.
I am just giving tips for the given constraint to scrollview.
First take scrollview and fit to the screen and give constraint as follows
Now take a view inside scrollview and fit to scrollview and give constraint like
Still its giving error of constraint. So now, give equal width of view to scrollview
and now you can take as many control as you want and give constraints as normal as we give to other controls.
Hope you understand well.
Enjoy Coding.
The reason is that scrollView is too long and hidden below the screen and contentSize isn't configured properly.
A lazy solution is to set them properly in code when twoViewController appears.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let width = self.scrollView.frame.width
scrollView.frame = CGRect(origin: scrollView.frame.origin,
size: CGSize(width: width, height: CGFloat(500)))
// Make sure this height is longer than scrollView.
scrollView.contentSize = CGSize(width: width, height: CGFloat(2000))
}
The proper solution (and correct way to implement a scrollview) is to fix scrollable content size ambiguity (auto layout errors in storyboard) like what #Jitendra Modi mentioned.
I have a question to use UITableView in xib file attaching to UIScrollView with checked paging enabled.
I am not native speaker in english, so please see this carefully.
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var menuScrollView: UIScrollView!
#IBOutlet var pageScrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let V1: View1 = View1(nibName: "View1", bundle: nil)
let V2: View2 = View2(nibName: "View2", bundle: nil)
self.addChildViewController(V1)
self.pageScrollView.addSubview(V1.view)
V1.didMoveToParentViewController(self)
self.addChildViewController(V2)
self.pageScrollView.addSubview(V2.view)
V2.didMoveToParentViewController(self)
var V2Frame: CGRect = V2.view.frame
V2Frame.origin.x = self.view.frame.width
V2.view.frame = V2Frame
print("view width:",self.view.frame.width, "v2 origin x:",V2.view.frame.origin.x)
self.pageScrollView.contentSize = CGSizeMake(self.view.frame.width * 2, self.pageScrollView.frame.height)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The first page shows the little bit of second page but I coded ,as shown as above code,
V2Frame.origin.x = self.view.frame.width
I thought this would work to fit width of the view but it doesn't.
I want the View1 and View2 to fit the UIScrollView.
I really appreciate to give me a help.
I did some research but I could not find an appropriate answer regarding the following question.
I don't use the interface builder to generate my views so every control is defined programmatically like so :
let loginButton: UIButton = {
let button = UIBUtton()
button.setTitle("Title", forState: .Normal)
// adding some properties to my button
return button
}()
Then I add it to the view of my ViewController like that :
self.view.addSubview(loginButton)
And I add some constraints with the Visual Format Language
For example :
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat
_("V:|[v0(25)]|", options:NSLayoutFormatOptions(), metrics: nil,
_views: ["v0":loginButton]))
It works really well and I prefer it that way(over the IB) but the main issue is that I end up with really big ViewController classes as I define every controls in the ViewControllers classes.
I guess I should have all the controls declarations, constraints,... in a separate file but I can't find how to do that.
I tried to create a separate class, for example LoginView, which contains all the controls, and a function setupViews(superView: UIView) that adds subviews to the superView parameter and specify constraints. Then in the viewDidLoad() method of my ViewController, I instantiate this class (LoginView) and execute the setupView(superView: self.view). It looks like this :
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let loginView = LoginView()
loginView.setupViews(self.view)
}
}
I am pretty sure this is not the way to go. It also causes problems when I try to addTarget to some of my controls.
My question, what is the best way to do it, following the MVC pattern.
Thank you very much !
EDIT 1 :
As asked in the comments, here is the implementation of the LoginView class (I only kept few controls/constraints to keep it short but it is the same process for every other one)
import UIKit
class LoginView {
let superView: UIView?
init(tempSuperView: UIView){
superView = tempSuperView
}
let usernameTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.textColor = UIColor.whiteColor()
return textField
}()
let loginButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Login", forState: .Normal)
return button
}()
//some more controls definition
func setupViews() -> Void {
superView!.addSubview(usernameTextField)
superView!.addSubview(loginButton)
superView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat
_("V:|-150-[v0(50)]", options:NSLayoutFormatOptions(), metrics: nil,
_views: ["v0":usernameTextField]))
superView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat
_("H:|-150-[v0(25)]", options:NSLayoutFormatOptions(), metrics: nil,
_views: ["v0":usernameTextField]))
//same for the button
}
}
Here is my ViewController :
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let loginView = LoginView(tempSuperView: self.view)
loginView.setupViews()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I'm trying to make a scroll view on iOS with Swift and Autolayout, but the screen crop the content instead of resizing it.
My layout and constraints
How can i fix this ?
EDIT : Tried with UITextView it doesn't work too
EDIT 2 : I'm trying to make a screen like that and my problem is that i can't implements this screen for all screens resolution and rotation with AutoLayout
I found a solution !
Use Interface Builder only to add a UIScrollView and add constraints to fit superview, and create all subviews programatically.
Override updateViewConstraints()and for each view in the ScrollView set translatesAutoresizingMaskIntoConstraints to false and add a width constraint to keep scrollview width. Dont forget to call super.updateViewConstraint().
Override didRotateFromInterfaceOrientation() and viewDidAppear() and set scrollView.contentSize.height to wrap all items (if you don't do that, you can't scroll).
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
var constraintsUpdated = false;
var label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
label.numberOfLines = 0
label.textColor = UIColor.blackColor()
scrollView.addSubview(label)
}
override func updateViewConstraints() {
if(constraintsUpdated){
//Rotate, just update view and keep constraints
super.updateViewConstraints()
scrollView.layoutSubviews()
return;
}
var viewsDictionary = ["label": label, "scrollView": scrollView]
let screenWidth = self.view.frame.width
label.setTranslatesAutoresizingMaskIntoConstraints(false)
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(String(format: "H:[label(==scrollView)]", screenWidth), options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary))
constraintsUpdated = true
super.updateViewConstraints()
//Update subviews
scrollView.layoutSubviews()
}
override func viewDidAppear(animated: Bool) {
scrollView.contentSize.height = label.bounds.height
}
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
scrollView.contentSize.height = label.bounds.height
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I found another solution with FLKAutoLayout library, that's work great with less code!
class ViewController: UIViewController {
var labelTitle: UILabel = UILabel()
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
//TITLE
scrollView.addSubview(labelTitle)
labelTitle.textColor = UIColor.blackColor()
labelTitle.numberOfLines = 0
//constraints
labelTitle.constrainWidthToView(self.view, predicate: nil)
labelTitle.numberOfLines = 0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidLayoutSubviews() {
scrollView.contentSize.height = labelTitle.frame.height
}
}
I don't know if it's a UIScrollView bug with AutoLayout or a feature