I am implementing a screen that shows a complete question
For Example:
"First day of the week is .........."
when the user touch the dots an alert appears and allows user to enter an answer to submit and replace the dots with his answers
My steps are:
1. I generated ranges for the dots by :
func getRangesOfMissingWords(by missingWord: String = completePlace) -> [(answer: String, range: NSRange)]? {
let body: NSString = theQuestionBody.text as NSString
var ranges : [(answer: String, range: NSRange)] = []
var searchRange = NSRange(location: 0, length: body.length)
while searchRange.location < body.length {
searchRange.length = body.length - searchRange.location
let foundRange = self.getRangeOf(word: missingWord, in: searchRange)
if foundRange != nil {
ranges.append((answer: missingWord, range: foundRange!))
searchRange.location = foundRange!.location + foundRange!.length
}
else {
break
}
}
if ranges.isEmpty == false {
return ranges
}
else {
return nil
}
}
and then
2.Generated a frames around the missing word ranges by:
func createAnswersFrames(from ranges: [(answer: String, range: NSRange)]) {
var frames : [WordFrame] = []
for range in ranges {
let pos2 = self.theQuestionBody.position(from: self.theQuestionBody.beginningOfDocument, offset: (range.range.location + (range.range.length - 1)))
let pos1 = self.theQuestionBody.position(from: self.theQuestionBody.beginningOfDocument, offset: range.range.location)
print("pos1 : \(pos1.debugDescription)")
print("pos2 : \(pos2.debugDescription)")
let textRange : UITextRange = self.theQuestionBody.textRange(from: pos1!, to: pos2!)!
let frame: CGRect = self.theQuestionBody.firstRect(for: textRange)
frames.append(WordFrame(rect: frame))
}
self.missingWordsFrames = frames
}
and
3.added a UITapGestureRecognizer for the UITextView
let sigleTap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(tapRecognizer:)))
theQuestionBody.addGestureRecognizer(sigleTap)
4.and in the observer for the tap
func handleTap(tapRecognizer: UITapGestureRecognizer) {
guard let ranges = self.missingWordsRanges else {
return
}
self.createAnswersFrames(from: ranges)
let touchPoint = tapRecognizer.location(in: theQuestionBody)
if self.missingWordsFrames.count > 0 {
for (index,frame) in self.missingWordsFrames.enumerated() {
frame.isSelected = false
let validFrame = frame.rect
if (validFrame.contains(touchPoint)) {
frame.isSelected = true
let alertTitle = Helper.getSerroundingText(of: self.missingWordsRanges![index].range, from: self.question.text)
let alert = UIAlertController(title: alertTitle , message:"", preferredStyle: .alert)
alert.addTextField { (textField) in
textField.placeholder = "Complete..."
}
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] (_) in
let textField = alert!.textFields![0]
textField.becomeFirstResponder()
let answer = textField.text
if answer != "" {
self.submitAnswer(answer!)
}
}))
self.present(alert, animated: true, completion: nil)
break
}
}
}
}
The problem is that the frames work fine for the first time the view is appeared, but when the user insert an answer that has long number of characters so the rest of the text will be placed in a new line the frames doesn't work fine then .
Example
"The x is a .......... . And the y is .........."
answer -> "SomeLongWordAnswer"
"The x is a SomeLongWordAnswer . And the y is
.........."
Here the frame for the second dots stays on the first line as it was !