【问题标题】:weird positioning of the cursor in the UITextView光标在 UITextView 中的奇怪定位
【发布时间】:2016-10-03 21:38:06
【问题描述】:

我正在我的swift 应用程序中从头开始编写UITextView。 我在view 上放了一个textView,如下所示:

它就在键盘的正上方。

textView 具有附加到 view 的约束:leadingbottomtoptrailing,全等于 = 4

view 具有以下约束:

trailingleadingbottomtopheight

Height 是我代码中的一个出口。我正在检查textView 中有多少行,并据此修改height

func textViewDidChange(textView: UITextView) { //Handle the text changes here
    switch(textView.numberOfLines()) {
    case 1:
        heightConstraint.constant = 38
        break
    case 2:
        heightConstraint.constant = 50
        break
    case 3:
        heightConstraint.constant = 70
        break
    case 4:
        heightConstraint.constant = 90
        break
    default:
        heightConstraint.constant = 90
        break
    }
}

上面的行数是由这个扩展计算的:

extension UITextView{

  func numberOfLines() -> Int{
      if let fontUnwrapped = self.font{
          return Int(self.contentSize.height / fontUnwrapped.lineHeight)
      }
      return 0
  }
}

textView 的初始高度为38textView中的初始字体大小为15

现在,当用户开始输入新行时,它工作得很好,但 textView 没有设置在视图的完整范围内。我的意思是,它看起来像这样:

它应该是这样的:

为什么要添加这个额外的空白,我该如何摆脱它?

当前当新行出现时有这个空白,但是当用户滚动 textView 以使文本居中并摆脱空白时 - 它永远消失了,用户无法再次向上滚动,所以白色线在那里。所以对我来说,刷新内容似乎有些问题,但也许你知道得更好——你能给我一些提示吗?

【问题讨论】:

  • 我不太清楚,根据我的猜测将 ScrollingEnabled 更改为 NO 可能会起作用..
  • hmm @Gokul 也许会有所帮助,但我需要 scrollingEnabledtrue 因为如果文本超过 4 行,那么 textView 不再增长,用户可以滚动里面的内容...
  • 您是否为聊天构建了应用程序?
  • 不,否则我很确定有一些已经内置的组件......我创建了这个字段用于添加 cmets
  • @user3766930 您确认行数正确吗?这意味着,如果您在文本框中的第 3 行输入,您的 numberOfLines() 方法是否返回 3。

标签: ios swift uitextview nslayoutconstraint


【解决方案1】:

这是我在我正在开发的一个应用程序的评论部分使用的一种不同的方法。这与 Facebook Messenger iOS 应用程序的输入字段非常相似。更改了出口名称以与您的问题中的名称相匹配。

//Height constraint outlet of view which contains textView.
@IBOutlet weak var heightConstraint: NSLayoutConstraint!
@IBOutlet weak var textView: UITextView!

//Maximum number of lines to grow textView before enabling scrolling.
let maxTextViewLines = 5
//Minimum height for textViewContainer (when there is no text etc.)
let minTextViewContainerHeight = 40

func textViewDidChange(textView: UITextView) {
    let textViewVerticalInset = textView.textContainerInset.bottom + textView.textContainerInset.top
    let maxHeight = ((textView.font?.lineHeight)! * maxTextViewLines) + textViewVerticalInset
    let sizeThatFits = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))

    if sizeThatFits.height < minTextViewContainerHeight {
        heightConstraint.constant = minTextViewContainerHeight
        textView.scrollEnabled = false
    } else if sizeThatFits.height < maxHeight {
        heightConstraint.constant = sizeThatFits.height
        textView.scrollEnabled = false
    } else {
        heightConstraint.constant = maxHeight
        textView.scrollEnabled = true
    }
}

func textViewDidEndEditing(textView: UITextView) {
    textView.text = ""
    heightConstraint.constant = minTextViewContainerHeight
    textView.scrollEnabled = false
}

【讨论】:

    【解决方案2】:

    我正在使用ASTextInputAccessoryView。它为您处理一切,并且非常容易设置:

    import ASTextInputAccessoryView
    
    class ViewController: UIViewController {
    
        var iaView: ASResizeableInputAccessoryView!    
        var messageView = ASTextComponentView()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let photoComponent = UINib
                .init(nibName: "PhotosComponentView", bundle: nil)
                .instantiateWithOwner(self, options: nil)
                .first as! PhotosComponentView
    
            messageView = ASTextComponentView(frame: CGRect(x: 0, y: 0, width: screenSize.width , height: 44))
            messageView.backgroundColor = UIColor.uIColorFromHex(0x191919)
            messageView.defaultSendButton.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
    
            iaView = ASResizeableInputAccessoryView(components: [messageView, photoComponent])
            iaView.delegate = self        
        }
    }
    
    //MARK: Input Accessory View
    extension ViewController {
        override var inputAccessoryView: UIView? {
            return iaView
        }
    
        // IMPORTANT Allows input view to stay visible
        override func canBecomeFirstResponder() -> Bool {
            return true
        }
    
        // Handle Rotation
        override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
            super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
    
            coordinator.animateAlongsideTransition({ (context) in
                self.messageView.textView.layoutIfNeeded()
            }) { (context) in
                self.iaView.reloadHeight()
            }
        }
    }
    
    // MARK: ASResizeableInputAccessoryViewDelegate
    extension ViewController: ASResizeableInputAccessoryViewDelegate {
    
        func updateInsets(bottom: CGFloat) {
            var contentInset = tableView.contentInset
            contentInset.bottom = bottom
            tableView.contentInset = contentInset
            tableView.scrollIndicatorInsets = contentInset
        }
    
        func inputAccessoryViewWillAnimateToHeight(view: ASResizeableInputAccessoryView, height: CGFloat, keyboardHeight: CGFloat) -> (() -> Void)? {
    
            return { [weak self] in
                self?.updateInsets(keyboardHeight)
                self?.tableView.scrollToBottomContent(false)
            }
        }
    
        func inputAccessoryViewKeyboardWillPresent(view: ASResizeableInputAccessoryView, height: CGFloat) -> (() -> Void)? {
            return { [weak self] in
                self?.updateInsets(height)
                self?.tableView.scrollToBottomContent(false)
            }
        }
    
        func inputAccessoryViewKeyboardWillDismiss(view: ASResizeableInputAccessoryView, notification: NSNotification) -> (() -> Void)? {
            return { [weak self] in
                self?.updateInsets(view.frame.size.height)
            }
        }
    
        func inputAccessoryViewKeyboardDidChangeHeight(view: ASResizeableInputAccessoryView, height: CGFloat) {
            let shouldScroll = tableView.isScrolledToBottom
            updateInsets(height)
            if shouldScroll {
                self.tableView.scrollToBottomContent(false)
            }
        }
    }
    

    现在您只需为AccessoryView 的按钮设置操作即可。

    // MARK: Actions
    extension ViewController {
    
        func buttonAction(sender: UIButton!) {
    
            // do whatever you like with the "send" button. for example post stuff to firebase or whatever
            // messageView.textView.text <- this is the String inside the textField
    
            messageView.textView.text = ""
        }
    
        @IBAction func dismissKeyboard(sender: AnyObject) {
            self.messageView.textView.resignFirstResponder()
        }
    
        func addCameraButton() {
    
            let cameraButton = UIButton(type: .Custom)
            let image = UIImage(named: "camera")?.imageWithRenderingMode(.AlwaysTemplate)
            cameraButton.setImage(image, forState: .Normal)
            cameraButton.tintColor = UIColor.grayColor()
    
            messageView.leftButton = cameraButton
    
            let width = NSLayoutConstraint(
                item: cameraButton,
                attribute: .Width,
                relatedBy: .Equal,
                toItem: nil,
                attribute: .NotAnAttribute,
                multiplier: 1,
                constant: 40
            )
            cameraButton.superview?.addConstraint(width)
    
            cameraButton.addTarget(self, action: #selector(self.showPictures), forControlEvents: .TouchUpInside)
        }
    
        func showPictures() {
    
            PHPhotoLibrary.requestAuthorization { (status) in
                NSOperationQueue.mainQueue().addOperationWithBlock({
                    if let photoComponent = self.iaView.components[1] as? PhotosComponentView {
                        self.iaView.selectedComponent = photoComponent
                        photoComponent.getPhotoLibrary()
                    }
                })
            }
        }
    }
    

    【讨论】:

    • downvote 至少在乎评论?我发誓,如果这是同时回答的“战术性否决票”,业力会摧毁你
    猜你喜欢
    • 1970-01-01
    • 2019-05-26
    • 1970-01-01
    • 1970-01-01
    • 2011-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多