A swift game of words, part I
I wanted to create a simple app to make learning vocab for my kids a little more fun and at the same time use the new drag and drop methods in iOS 11.
I ended up with this and thought I would share it with you.

You have two tabs, the first is intended to provide an area you can type and edit words in, it is a simple tableview. The second jumbles the letters in each of the words you have entered in the tableview presenting them one by one, inviting the user to re-arrange the letters to get the spelling right.
Of course its not perfect; I didn’t put the editing in the tableview yet and it doesn’t tell you when you got it correct, but the barebones are there and besides which I didn’t want to make this article too long. Long rambling technical tutorials I just not my thing.
How does it work, here goes. First you need a pick tab based app in Xcode, and having created it go to the VC managing the first tab. Within which you can add the following code.
var myTableView: UITableView = UITableView()override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)// Get main screen bounds let screenSize: CGRect = UIScreen.main.bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
myTableView.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
myTableView.dataSource = self
myTableView.delegate = self
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: “myCell”)
self.view.addSubview(myTableView)
}func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return itemsToLoad.count
}func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: “myCell”, for: indexPath as IndexPath)cell.textLabel?.text = itemsToLoad[indexPath.row]
return cell
}
It’s all pretty vanilla code, perhaps the most important aspect the common swift class data file it references, within which I put the core data structure a simple array.
var itemsToLoad: [String] = [“dog”, “cat”, “mat”,”sat”]
A technic rightly or wrongly I find myself using again and again. Moving ahead to the second tab’s View Controller, you need to start off by creating the button to switch data sets. You do so with this code.
override func viewDidLoad() {super.viewDidLoad()
if firstRun {
firstRun = false
let button = UIButton()
button.setTitle(“Next”, for: .normal)
button.setTitleColor(UIColor.blue, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(pressed), for: .touchUpInside)
self.view.addSubview(button)
button.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 128).isActive = true
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
loadScramble(word2L: firstWord)
}
}@objc func pressed(sender: UIButton!) {
whiteWin.removeFromSuperview()
if firstWord < itemsToLoad.count — 1 {
firstWord = firstWord + 1
} else {
firstWord = 0
}
loadScramble(word2L: firstWord)
}
The firstRun bool ensures it is created just once; the button itself cycles around the itemsToLoad array of words and presents them to be unscrambled thru the loadScramble method one by one.
The code for loadScamble mthod in the meantime looks like this. We start with a stackview, to which we add the letters individually for each word to fixed sizes boxes.
func loadScramble(word2L: Int) {
whiteWin = UIStackView()
whiteWin.axis = UILayoutConstraintAxis.horizontalwhiteWin.distribution = UIStackViewDistribution.equalSpacing
whiteWin.alignment = UIStackViewAlignment.center
whiteWin.spacing = 8.0
self.view.addSubview(whiteWin)
whiteWin.translatesAutoresizingMaskIntoConstraints = false
whiteWin.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true let demo = itemsToLoad[word2L]
let letters = Array(demo) let position2S = (self.view.bounds.width — CGFloat((letters.count * 40))) / 2 let margins = view.layoutMarginsGuide whiteWin.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: position2S).isActive = true let shuffled = (letters as NSArray).shuffled() for letters in shuffled {
let letterView = UILabel()
letterView.font = letterView.font.withSize(32)
letterView.text = “\(letters)”
letterView.backgroundColor = UIColor.lightGray
letterView.textAlignment = .center// drag and drop doesn’t work unless interactive letterView.isUserInteractionEnabled = true
whiteWin.addArrangedSubview(letterView)
let dragInteraction = UIDragInteraction(delegate: self)
letterView.addInteraction(dragInteraction)
let dropInteraction = UIDropInteraction(delegate: self)
letterView.addInteraction(dropInteraction)
letterView.widthAnchor.constraint(equalToConstant: 32).isActive = true
letterView.heightAnchor.constraint(equalToConstant: 32).isActive = true
letterView.translatesAutoresizingMaskIntoConstraints = false
}
}
We also add the new drag and drop delegate interactions [don’t forget to add the delegates to the class method].
Which brings us to the new code part, the drag and drop methods.
var dragLocation: UIView!func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] { let letter2M = interaction.view as? UILabel
let letter2T = letter2M?.text as NSString?
for boxes in self.whiteWin.arrangedSubviews {
if (boxes.frame.contains(session.location(in: self.whiteWin)) ) {
dragLocation = boxes
}
} let provider = NSItemProvider(object: letter2T!)
let item = UIDragItem(itemProvider: provider)
return [item]
}func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) { session.loadObjects(ofClass: NSString.self) {object in
let text2D = object as! [String]
let dropLocation = session.location(in: self.whiteWin) for boxes in self.whiteWin.arrangedSubviews {
if (boxes.frame.contains(dropLocation) ) {
(self.dragLocation as? UILabel)?.text = (boxes as? UILabel)?.text
(boxes as? UILabel)?.text = text2D.first
}
}
}
}func dropInteraction(_ interaction: UIDropInteraction,sessionDidUpdate session: UIDropSession) -> UIDropProposal { return UIDropProposal(operation: .move)}
The drag method captures the source label and the text on it and passes across to the drop method, the drop makes the swap. And there you have it. I will finish the tableview in the coming days and add some code to check the answers, will post a followup story to this one.