11

I am implementing a control that behaves like an NSTokenField (the address picker in Mail, for example) for use in iOS. I use a horizontal UICollectionView and my rightmost cell is a UITextField. This control will appear in a form with other textfields that have right-aligned text. For my new control to look right in this context, I'd like for the UITextField to always be at the right edge of the UICollectionView and for selected tags to appear to the left of it.

At the moment, when I first click into this row, the textfield scrolls to the left and then it gets pushed to the right as more tags are added. Once it is flush with the right edge of the UICollectionView (when 'Appetizer' is added in the image below), then I start getting the behavior I want.

Tag selection

I've been thinking about something like a minimum width on the UITextField so it takes up as much width as is available at first and less and less as tags are added. Or alternately, some way to pin the fixed-with field to the right. I haven't found a way to implement these ideas, though!

How can I make it so the textfield is always at the right of the UICollectionView?

Hélène Martin
  • 1,409
  • 2
  • 15
  • 42

3 Answers3

5

Here is a solution where the UITextField is not part of the UICollectionView.

enter image description here

I added the collectionView and textField on my storyboard with the following layout constraints: CollectionView: Leading to superview, Height, Top to superview, horizontal spacing with textField. TextField: Height equal to collectionView, Trailing to superview, Width = 100, horizontal spacing with collectionView.

I created an IBOutlet for the width constraint of the textField. The width is changed dynamically as text is entered.

Swift 3 Sample Code

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var textFieldWidthConstraint: NSLayoutConstraint!

    var textArray: [String] = [String]()

    @IBAction func editingChanged(_ sender: AnyObject) {
        let size = textField.sizeThatFits(textField.frame.size)
        textFieldWidthConstraint.constant = max(size.width,100)

        if textArray.count > 0 {
            self.collectionView.scrollToItem(at: IndexPath(row: self.textArray.count-1, section: 0), at: .right, animated: true)
        }
    }

    @IBAction func addText(_ sender: AnyObject) {
        if textField.text?.characters.count ?? 0 > 0 {
            textArray.append(textField.text!)
            textField.text = ""
            textFieldWidthConstraint.constant = 100
            let newIndexPath = IndexPath(row: textArray.count-1, section: 0)
            collectionView.insertItems(at: [newIndexPath])
            collectionView.performBatchUpdates({
                }, completion: { (_) in
                    self.collectionView.scrollToItem(at: newIndexPath, at: .right, animated: true)
            })
        }
    }

}
Carien van Zyl
  • 2,853
  • 22
  • 30
  • I too was thinking about the same... This answer is a nice workaround. It will give the effect of what you require. @Hélène Martin – jeet.chanchawat Oct 07 '16 at 12:26
  • Thank you very much @carien-van-zyl. I will play around with this in the next few days. I apologize for the bounty expiry -- I was away for a couple of days! – Hélène Martin Oct 08 '16 at 14:07
0

As part of UICollectionViewDataSource

Try using

// The view that is returned must be retrieved from a call to -dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;

And define your view that contains the textfield as a footer.

iCyberPaul
  • 650
  • 4
  • 15
0

For the sake of completeness and in case it gives someone an idea, I ended up simply forcing right-to-left order on the UICollectionView as described in this answer. It means new tags appear directly to the left of the UITextField which worked for my needs.

Hélène Martin
  • 1,409
  • 2
  • 15
  • 42