how to fix the wrong word wrap? - swift

for a very long time I can’t solve a simple problem, namely: transferring a word to a new line and automatically reducing the label if the word does not fit. Tell me how to be in this situation.
#IBOutlet weak var wordLabel: UILabel!
#IBOutlet weak var transcriptionLabel: UILabel!
#IBOutlet weak var translationLabel: UILabel!
var index = 0
var word = ""
var transcription = ""
var translation = ""
override func viewDidLoad() {
super.viewDidLoad()
wordLabel.text = word
transcriptionLabel.text = ""
translationLabel.text = ""
wordLabel.adjustsFontSizeToFitWidth = true
wordLabel.minimumScaleFactor = 0.1
transcriptionLabel.adjustsFontSizeToFitWidth = true
transcriptionLabel.minimumScaleFactor = 0.1
translationLabel.adjustsFontSizeToFitWidth = true
translationLabel.minimumScaleFactor = 0.1
self.transcriptionLabel.alpha = 0.0
self.translationLabel.alpha = 0.0
}

Thanks humblePilgrim, for the suggested answer, it really helped. I leave the link to the source and attach the answer itself.
source
Swift 4.2
extension UILabel {
// Adjusts the font size to avoid long word to be wrapped
func fitToAvoidWordWrapping() {
guard adjustsFontSizeToFitWidth else {
return // Adjust font only if width fit is needed
}
guard let words = text?.components(separatedBy: " ") else {
return // Get array of words separate by spaces
}
// I will need to find the largest word and its width in points
var largestWord: NSString = ""
var largestWordWidth: CGFloat = 0
// Iterate over the words to find the largest one
for word in words {
// Get the width of the word given the actual font of the label
let wordWidth = word.size(withAttributes: [.font: font]).width
// check if this word is the largest one
if wordWidth > largestWordWidth {
largestWordWidth = wordWidth
largestWord = word as NSString
}
}
// Now that I have the largest word, reduce the label's font size until it fits
while largestWordWidth > bounds.width && font.pointSize > 1 {
// Reduce font and update largest word's width
font = font.withSize(font.pointSize - 1)
largestWordWidth = largestWord.size(withAttributes: [.font: font]).width
}
}
}
I thank user Dam for the answer.

Related

Swift Timer Loop doesn't trigger but view and controller are connected

I'm trying to print the title "⚡️FlashChat" one letter at a time.
I'm coding along with a video and I've checked my code against the instructor's and it matches.
Right now, the title prints in its entirety when the sim is built.
I've adjusted the withTimeInterval to see fit that was set too quickly.
If I remove the loop and just execute the titleLabel.text = "", the title disappears, so it is connected correctly.
Thanks in advance!
import UIKit
class WelcomeViewController: UIViewController {
#IBOutlet weak var titleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
titleLabel.text = ""
var charIndex = 0.0
let titleText = "⚡FlashChat"
for letter in titleText {
Timer.scheduledTimer(withTimeInterval: 7.0
* charIndex, repeats: false) {(timer)
in
self.titleLabel.text?.append(letter)}
}
charIndex += 1
}
}
You're incrementing charIndex outside your for loop. So interval will always be equal to 0

Drawing boxes around each digit to be entered in UITextField

I am trying to draw boxes around each digit entered by a user in UITextField for which keyboard type is - Number Pad.
To simplify the problem statement I assumed that each of the digits (0 to 9) will have same bounding box for its glyph, which I obtained using below code:
func getGlyphBoundingRect() -> CGRect? {
guard let font = font else {
return nil
}
// As of now taking 8 as base digit
var unichars = [UniChar]("8".utf16)
var glyphs = [CGGlyph](repeating: 0, count: unichars.count)
let gotGlyphs = CTFontGetGlyphsForCharacters(font, &unichars, &glyphs, unichars.count)
if gotGlyphs {
let cgpath = CTFontCreatePathForGlyph(font, glyphs[0], nil)!
let path = UIBezierPath(cgPath: cgpath)
return path.cgPath.boundingBoxOfPath
}
return nil
}
I am drawing each bounding box thus obtained using below code:
func configure() {
guard let boundingRect = getGlyphBoundingRect() else {
return
}
for i in 0..<length { // length denotes number of allowed digits in the box
var box = boundingRect
box.origin.x = (CGFloat(i) * boundingRect.width)
let shapeLayer = CAShapeLayer()
shapeLayer.frame = box
shapeLayer.borderWidth = 1.0
shapeLayer.borderColor = UIColor.orange.cgColor
layer.addSublayer(shapeLayer)
}
}
Now problem is -
If I am entering digits - 8,8,8 in the text field then for first occurrence of digit the bounding box drawn is aligned, however for second occurrence of same digit the bounding box appears a bit offset (by negative x), the offset value (in negative x) increases for subsequent occurrences of same digit.
Here is image for reference -
I tried to solve the problem by setting NSAttributedString.Key.kern to 0, however it did not change the behavior.
Am I missing any important property in X axis from the calculation due to which I am unable to get properly aligned bounding box over each digit? Please suggest.
The key function you need to use is:
protocol UITextInput {
public func firstRect(for range: UITextRange) -> CGRect
}
Here's the solution as a function:
extension UITextField {
func characterRects() -> [CGRect] {
var beginningOfRange = beginningOfDocument
var characterRects = [CGRect]()
while beginningOfRange != endOfDocument {
guard let endOfRange = position(from: beginningOfRange, offset: 1), let textRange = textRange(from: beginningOfRange, to: endOfRange) else { break }
beginningOfRange = endOfRange
var characterRect = firstRect(for: textRange)
characterRect = convert(characterRect, from: textInputView)
characterRects.append(characterRect)
}
return characterRects
}
}
Note that you may need to clip your rects if you're text is too long for the text field. Here's an example of the solution witout clipping:

Can't disable word wrap of NSTextView

I have tried the answers in How to disable word-wrap of NSTextView?
for half a day but have had no luck. The answers were a bit scattered and confusing really.
I have this code:
#IBOutlet var display: NSTextView!
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
let LargeNumberForText: CGFloat = 1.0e7
display.textContainer!.containerSize = NSMakeSize(LargeNumberForText, LargeNumberForText)
display.textContainer!.widthTracksTextView = false
display.horizontallyResizable = true
display.autoresizingMask = [.ViewWidthSizable, .ViewHeightSizable]
}
and I have this in the .xib:
Did I miss a step?
My issue was in fact Premature line wrapping in NSTextView when tabs are used I corrected the issue by employing both the word-wrap code above, and calling this after changing the text:
func format() {
let numStops = 100000;
let tabInterval = 40;
var tabStop: NSTextTab
//attributes for attributed String of TextView
let paraStyle = NSMutableParagraphStyle()
// This first clears all tab stops, then adds tab stops, at desired intervals...
paraStyle.tabStops = []
for cnt in 1...numStops {
tabStop = NSTextTab(type: .LeftTabStopType, location: CGFloat(tabInterval * cnt))
paraStyle.addTabStop(tabStop)
}
var attrs = [String: AnyObject]()
attrs.updateValue(paraStyle, forKey:NSParagraphStyleAttributeName)
display.textStorage!.addAttributes(attrs, range: NSMakeRange(0, display.textStorage!.string.characters.count))
}
where display is the NSTextView. Subclassing would be more elegant of course.

Swift refactoring assistance

I have a meter laid out on a storyboard that is comprised of ten segments. There are an additional ten segments on top of the original segments that are colored to simulate a lighted segment - these are outlets. (See image below.) Currently I am using a switch statement to hide and unhide each outlet/segment based on a constantly varying input level. But, as you can see in the code below, it's not pretty. I keep reading that polymorphism is often the way to improve a switch statement, but I can't see how that would help here.
switch input {
case 0...9:
seg1.hidden = false
seg2.hidden = true
seg3.hidden = true
seg4.hidden = true
seg5.hidden = true
seg6.hidden = true
seg7.hidden = true
seg8.hidden = true
seg9.hidden = true
seg10.hidden = true
case 10...19:
seg1.hidden = false
seg2.hidden = false
seg3.hidden = true
seg4.hidden = true
seg5.hidden = true
seg6.hidden = true
seg7.hidden = true
seg8.hidden = true
seg9.hidden = true
seg10.hidden = true
...
and on and on for eight more levels.
//You can have these segments in an array like this-
let segments = [seg1, seg2, seg3, seg4, seg5,seg6, seg7, seg8, seg9, seg10]
// Function that will setup the segments based on input
func setUpSegmentsForInputValue(segments:[UIView], value:Int) {
for (index, segment) in segments.enumerate() {
segment.hidden = (value/10) != index
}
}
// Call the function with whatever input values
setUpSegmentsForInputValue(segments: segments, value: someValue)
You could add all of your outlets to an array. Map the input to an "index range" and then decide in a for loop if outlet is in "index range". Then set hidden property accordingly.
#IBOutlet weak var seg1 = UIView()
#IBOutlet weak var seg2 = UIView()
#IBOutlet weak var seg3 = UIView()
#IBOutlet weak var seg4 = UIView()
...
let segs = [seg1, seg2, seg3, seg4]
let input = 19
let range = 0 ... input / 10
for (index, seg) in segs.enumerate() {
if range ~= index {
seg.hidden = false
}else {
seg.hidden = true
}
}
Alternative (instead of the for loop):
#IBOutlet weak var seg1 = UIView()
#IBOutlet weak var seg2 = UIView()
#IBOutlet weak var seg3 = UIView()
#IBOutlet weak var seg4 = UIView()
...
let segs = [seg1, seg2, seg3, seg4]
let input = 19
let range = 0 ... input / 10
segs.enumerate().forEach { index, seg in
seg.hidden = !(range ~= index)
}
Hope this helps.

Check text field Live

I have found this answer How to check text field input at real time?
This is what I am looking for. However I am having trouble actually implementing this code. Also my current geographical location makes googling almost impossible.
I want to be able to change the background color of the next text field if the correct number is entered into the previous text field. textfieldTwo background color will change to green if the correct value is entered in textFieldOne. If the value is incorrect then nothing will happen. Please help me out. I have two text fields called textFieldOne and textFieldTwo and nothing else in the code.
Just pop this in your main view controller in an empty project (try using iphone 6 on the simulator)
import UIKit
class ViewController: UIViewController {
var txtField:UITextField!
var txtFieldTwo:UITextField!
var rightNumber = 10
override func viewDidLoad() {
super.viewDidLoad()
//txtFieldOne
var txtField = UITextField()
txtField.frame = CGRectMake(100, 100, 200, 40)
txtField.borderStyle = UITextBorderStyle.None
txtField.backgroundColor = UIColor.blueColor()
txtField.layer.cornerRadius = 5
self.view.addSubview(txtField)
//txtFieldTwo
var txtFieldTwo = UITextField()
txtFieldTwo.frame = CGRectMake(100, 150, 200, 40)
txtFieldTwo.borderStyle = UITextBorderStyle.None
txtFieldTwo.backgroundColor = UIColor.blueColor()
txtFieldTwo.layer.cornerRadius = 5
self.view.addSubview(txtFieldTwo)
txtField.addTarget(self, action: "checkForRightNumber", forControlEvents: UIControlEvents.AllEditingEvents)
self.txtField = txtField
self.txtFieldTwo = txtFieldTwo
}
func checkForRightNumber() {
let number:Int? = self.txtField.text.toInt()
if number == rightNumber {
self.txtFieldTwo.backgroundColor = UIColor.greenColor()
} else {
self.txtFieldTwo.backgroundColor = UIColor.blueColor()
}
}
}
EDIT: Adding a version with IBOutlets and IBActions
Note that in this example the IBAction is connected to txtFieldOne on Sent Events / Editing Changed
Also, make sure your Text Fields border colors are set to None. In the storyboard, the way to do this is to choose the left most option with the dashed border around it. That's so you can color the backgrounds. You can use layer.cornerRadius to set the roundness of the border's edges.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txtField: UITextField!
#IBOutlet weak var txtFieldTwo: UITextField!
var rightNumber = 10
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func checkForRightNumber(sender: AnyObject) {
let number:Int? = self.txtField.text.toInt()
if number == rightNumber {
self.txtFieldTwo.backgroundColor = UIColor.greenColor()
} else {
self.txtFieldTwo.backgroundColor = UIColor.blueColor()
}
}
}