11 Replies
      Latest reply on Dec 9, 2019 2:41 PM by Relbot
      Relbot Level 1 Level 1 (0 points)

        Hey,

         

        I am trying to create a ViewController with a UILabel in it. The UIlabel has multiply lines and a fixed font size.

        On different devices, different amount of text can fit in this Label. Now my question is if not all of the text fits into the UILabel, how can I pass this remaining text to the next page.

         

        Thank you for your help

        Yours sincerelly

        Tell T.

        • Re: Pass on the part which doesn't fit into the UILabel
          Claude31 Level 8 Level 8 (7,255 points)

          The best wouyld probably to replace the label by a TextView, so that user can scroll content.

           

          Otherwise, read this:

          https://stackoverflow.com/questions/3077109/how-to-check-if-uilabel-is-truncated

           

          Otherwise, try to calculate where the label is truncated and select the substring to display in next view. But what do you do if label requires 5 views ?

          Use

          func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRect

           

          May read this:

          h ttps://forums.xamarin.com/discussion/67291/how-to-get-the-range-where-uilabel-will-truncate-a-text

            • Re: Pass on the part which doesn't fit into the UILabel
              Relbot Level 1 Level 1 (0 points)

              Thx for your fast answer,

               

              But isn't it possible to do it like the App "Books" from Apple. Depending on the device, different amount of text fits on one page.

               

              your sincerelly

              Tell

                • Re: Pass on the part which doesn't fit into the UILabel
                  Claude31 Level 8 Level 8 (7,255 points)

                  I looked further, using advice form h ttps://medium.com/@Bitomule/getting-text-size-on-ios-bdae7521822f

                  The following gives the required height.

                  Compare with the screenSize, and you'll find how much exceeds.

                  Up to you after this to repeat the computation to find where best to cut text.

                   

                            let attributes = [NSAttributedString.Key.font:self]
                            let attString = NSAttributedString(string: veryLongLabel.text!, attributes: attributes)
                            let framesetter = CTFramesetterCreateWithAttributedString(attString)
                            let width = Double(veryLongLabel!.bounds.width)
                            let x = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRange(location: 0,length: 0), nil, CGSize(width: width, height: 10000), nil)
                            print(x, veryLongLabel.bounds)

                  isn't it possible to do it like the App "Books" from Apple

                  Yes, but I'm not sure they use labels.

                  I did not have time to test this, but that may be the direction to look at.

                  https://stackoverflow.com/questions/51031829/how-to-turn-a-uitextview-into-pages-like-an-e-reader

                    • Re: Pass on the part which doesn't fit into the UILabel
                      Relbot Level 1 Level 1 (0 points)

                      I tested this https://stackoverflow.com/questions/51031829/how-to-turn-a-uitextview-into-pages-like-an-e-reader

                      Is almost works fine but the string I get returned is always a bit longer than the acutally text I can see on the device. Why is that?

                        • Re: Pass on the part which doesn't fit into the UILabel
                          Claude31 Level 8 Level 8 (7,255 points)

                          May be you have not defined correctly the font and there is a mismatch between the font used for computing the size and the font used for display.

                           

                          But if it's a small difference, can you correct it manually and arbitrarily ?

                            • Re: Pass on the part which doesn't fit into the UILabel
                              Relbot Level 1 Level 1 (0 points)

                              Yes, I almost solved it. It only works with specific font sizes and is there any possible way to use Fonts like "Avenir Next" instead of "Courier". This is not my taste

                              Do you know what Apple uses for "Books" and how they programed this?

                                • Re: Pass on the part which doesn't fit into the UILabel
                                  Claude31 Level 8 Level 8 (7,255 points)

                                  The way to make it work with any font is to pass the font as an attribute to func that compute size.

                                   

                                  So you used a TextView and not a label, isn't it ?

                                   

                                  If that's working, don't forget to close the thread.

                                    • Re: Pass on the part which doesn't fit into the UILabel
                                      Relbot Level 1 Level 1 (0 points)

                                      Yes I use a Textfield. So my current approach looks like this but this doesn't work properly. When the button got pressed it calculates the part of the string which wasn't visible on the screen. That works fine but the string I get from the function is always a little bit longer than the acutal text I can see on my device. What am I doing wrong?

                                      @IBAction func buttonHandler(_ sender: UIButton)
                                          {
                                              let substring = stringThatFitsOnScreen(originalString: textView.text)
                                              let textOfTextView = viewText
                                              passToNextView = String(textOfTextView.dropFirst(substring!.count))
                                              
                                              print(substring)
                                              print(passToNextView)
                                              performSegue(withIdentifier: "nextView", sender: self)
                                      
                                          }
                                          
                                          func stringThatFitsOnScreen(originalString: String) -> String? {
                                              // the visible rect area the text will fit into
                                              let userWidth  = textView.bounds.size.width - textView.textContainerInset.right - textView.textContainerInset.left
                                              let userHeight = textView.bounds.size.height - textView.textContainerInset.top - textView.textContainerInset.bottom
                                              let rect = CGRect(x: 0, y: 0, width: userWidth, height: userHeight)
                                      
                                              // we need a new UITextView object to calculate the glyphRange. This is in addition to
                                              // the UITextView that actually shows the text (probably a IBOutlet)
                                              let tempTextView = UITextView(frame: self.textView.bounds)
                                              tempTextView.font = textView.font
                                              tempTextView.text = textView.text
                                      
                                              // get the layout manager and use it to layout the text
                                              let layoutManager = tempTextView.layoutManager
                                              layoutManager.ensureLayout(for: tempTextView.textContainer)
                                      
                                              // get the range of text that fits in visible rect
                                              let rangeThatFits = layoutManager.glyphRange(forBoundingRect: rect, in: tempTextView.textContainer)
                                      
                                              // convert from NSRange to Range
                                              guard let stringRange = Range(rangeThatFits, in: originalString) else {
                                                  return nil
                                              }
                                      
                                              // return the text that fits
                                              let subString = originalString[stringRange]
                                              return String(subString)
                                          }
                        • Re: Pass on the part which doesn't fit into the UILabel
                          PBK Level 7 Level 7 (3,365 points)

                          You can build up the string and send it to

                                sizeWithAttributes:

                          https://developer.apple.com/documentation/foundation/nsstring/1531844-sizewithattributes?language=objc

                           

                          You can also determine the size of the UILabel in the current view (in the current device) using something like myLabel,frame.size.width.

                          When the string size is the size of the label you do something with the string you are creating like adding an "\n".

                            • Re: Pass on the part which doesn't fit into the UILabel
                              Relbot Level 1 Level 1 (0 points)

                              So my current approach looks like this but this doesn't work properly. When the button got pressed it calculates the part of the string which wasn't visible on the screen. That works fine but the string I get from the function is always a little bit longer than the acutal text I can see on my device. What am I doing wrong?

                                  @IBAction func buttonHandler(_ sender: UIButton)
                                  {
                                      let substring = stringThatFitsOnScreen(originalString: textView.text)
                                      let textOfTextView = labelText
                              
                                      passToNextView = String(textOfTextView.dropFirst(substring!.count))
                                      print(substring)
                                      print(passToNextView)
                                      print(textView.intrinsicContentSize.width)
                                      performSegue(withIdentifier: "test", sender: self)
                              
                                  }
                                  
                                  func stringThatFitsOnScreen(originalString: String) -> String? {
                                      // the visible rect area the text will fit into
                                      let userHeight = textView.bounds.size.height - textView.textContainerInset.top - textView.textContainerInset.bottom
                                      let userWidth  = textView.bounds.size.width - textView.textContainerInset.right - textView.textContainerInset.left
                                      print(userWidth)
                                      let userHeight = textView.bounds.size.height - textView.textContainerInset.top - textView.textContainerInset.bottom
                                      //let userWidth = textView.intrinsicContentSize.width
                                      //let userHeight = textView.intrinsicContentSize.height
                                      let rect = CGRect(x: 0, y: 0, width: userWidth, height: userHeight)
                              
                                      // we need a new UITextView object to calculate the glyphRange. This is in addition to
                                      // the UITextView that actually shows the text (probably a IBOutlet)
                                      let tempTextView = UITextView(frame: self.textView.bounds)
                                      tempTextView.font = textView.font
                                      tempTextView.text = textView.text
                              
                                      // get the layout manager and use it to layout the text
                                      let layoutManager = tempTextView.layoutManager
                                      layoutManager.ensureLayout(for: tempTextView.textContainer)
                              
                                      // get the range of text that fits in visible rect
                                      let rangeThatFits = layoutManager.glyphRange(forBoundingRect: rect, in: tempTextView.textContainer)
                              
                                      // convert from NSRange to Range
                                      guard let stringRange = Range(rangeThatFits, in: originalString) else {
                                          return nil
                                      }
                              
                                      // return the text that fits
                                      let subString = originalString[stringRange]
                                      return String(subString)
                                  }