Swift 2.1 - Making dynamic buttons with width relative to text length - ios

I need to make buttons programmatically based on the number of fetched values and each button needs to have width that is relative to their text length.
My current implementation has a fixed width with X position that also increments in fixed length.
What is the process of achieving this so I can have buttons like attached screenshot?
for var i = 0; i < self.category.count; i++ {
let frame1 = CGRect(x: 0 + (i * 45), y: 20, width: 80, height: 40 )
let button = UIButton(frame: frame1)
button.setTitle("\(category[i].name!)", forState: .Normal)
button.backgroundColor = UIColor.blackColor()
self.categoryScrollView.addSubview(button)
}

After you set text on button, use
button.sizeToFit()
then read the width of the button and add an appropriate number to it (this number will be same for all buttons regardless of the text length and will give you desired results.
Alternatively, you can add padding to button; in which case you only need to do the first step.

Related

Changing UILabel title in custom UIButton without positional change

I have a UIButton with the label on the left side and UIImageView on the right side. The button is used to open a UIPicker. When a value is picked in the picker the same value is shown in button title. When the title changes (or, more accurately, when to uilabel has a width that screws the UI up) the title and icon is moved and the UI does not look good.
When a title with too long text is used the word is clipped and when it's too short the alignment is messed up.
I've tried changing the label frame so it can be constant, whatever the text, and left aligning the text so the jumping stops. I added adjustsFontSizeToFitWidth = true which kind of works, but with a longer title the text will get too small. I've also tried recreating/rerendering the button when the title changes but all attempts fail.
lazy var sortButton = { () -> UIButton in
let btn = UIButton()
btn.addTarget(self, action: #selector(sortButtonPressed), for: .touchUpInside)
btn.setTitle(NSLocalizedString("Sortera", comment: ""), for: .normal)
btn.titleLabel?.text = btn.titleLabel?.text?.uppercased()
btn.setImage(UIImage(named: "ios-down"), for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitleColor(Colors.FILTER_BUTTON_TEXT_COLOR, for: .normal)
btn.titleLabel?.adjustsFontSizeToFitWidth = true
btn.titleLabel?.font = UIFont(name: Fonts.AkzidenzGroteskProMd, size: 16)
btn.backgroundColor = Colors.BUTTON_BACKGROUND_GRAY
btn.imageView?.contentMode = .scaleAspectFit
btn.imageEdgeInsets = UIEdgeInsets(top: 16, left: (btn.titleLabel?.frame.size.width)! - buttonInsideOffset/2, bottom: 16, right: -(btn.titleLabel?.frame.size.width)! + buttonInsideOffset/2)
btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(btn.titleLabel?.frame.size.width)! + buttonInsideOffset, bottom: 0, right: (btn.titleLabel?.frame.size.width)! - buttonInsideOffset)
return btn
}()
I want all button to like like this, whatever the title text:
However, when the text is too small it looks like this:
or when it's too long:
You can do it in many way but the simplest way :
Take a UIView and then others two-element (a label a imageView) set in this View and make it look like button then set constraint as you want. Then addTarget to label and do all functionality to that that target selector method.
If you dont want button image to be shifted to the right or left then you have to constraint independent of the button's title label
btn.imageView?.frame = CGRect(x: 0, y: 0, width: 20, height: 20) // Or any size you want
// NB: I ommited left insets intentionally
btn.imageEdgeInsets.top = 16
btn.imageEdgeInsets.bottom = 16
btn.imageEdgeInsets.right = 16
Then constraint your label dependent to the imageView position, that way only label size will change without affecting the position of image.
btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(btn.titleLabel?.frame.size.width)! + buttonInsideOffset, bottom: 0, right: btn.imageView?.frame.width + 10)
Lastly, Since the frame is fixed size, I think you need to limit the font scaling factor to the min size you want and truncate the tail when that size is reached. If you don't want to truncate tail then you have to enable multiline titleLabel (Which I think you don't want this).
btn.titleLabel?.minimumScaleFactor = 0.5 // Or whatever minimum scale you wish
btn.titleLabel?.lineBreakMode = NSLineBreakMode.byTruncatingTail // Since button size is fixed and you want to limit font size then the best option is to truncate tail

MDCChipField Swift - scrollable direction

I am trying to use MDCChipField, the material design component for Swift.
I am implementing the 'input chip' type and am able to add the entered text as a chip with
let mdcSearchField = MDCSearchField()
mdcSearchField.addChip(chipView)
When the chips overflow in MDCChipField, they get added to next row. How can i set the scrollable direction to horizontal instead of vertical?
In the link,
https://material.io/design/components/chips.html#input-chips,
The placement section explains
Input chips can be integrated with other components. They can appear:
- Inline with the text input cursor in a field
- In a stacked list
- In a horizontally scrollable list
How do i do a a horizontally scrollable list in code?
Thanks.
My approach was this:
Create a scrollable view
create an offset of say 10 for padding between chips
Add chips to the scrollable view
Set the frame origin of the chip with the declared offset
Add the chip's width to the offset plus the initial offset value (in this case 10)
Increase the content width of the scrollable view, set the width to the total
offset
(add more chips/repeat)
On chip removal (if needed)
Set initial offset back to 10
Remove chip from parent scrollable view
Loop through subviews and set the offset again to each of them
Set the scrollable view new width using the total offset
Here is a sample in Swift
Configure initial layout
class ChipSample
{
var tagXOffset = 10
var tagPadding = 10
func configureTagsView()
{
tagView = UIScrollView(frame: CGRect(x: 0, y: 120, width: view.bounds.width, height: 40))
view.addSubview(tagView)
}
Add a chip
func addChip(name:String)
{
let chip = MDCChipView()
chip.titleLabel.text = name
chip.setTitleColor(.gray, for: .normal)
chip.sizeToFit()
chip.addTarget(self, action: #selector(removeChip), for: .touchUpInside)
tagView.addSubview(chip)
chip.frame.origin.x = tagXOffset
chip.frame.origin.y = 0
tagXOffset += tagPadding + chip.frame.width
tagView.contentSize = CGSize(width: tagXOffset, height: tagViewHeight)
}
Remove the chip
#objc func removeChip(sender: MDCChipView!)
{
tagXOffset = tagPadding
sender.removeFromSuperview()
for subview in tagView.subviews {
subview.frame.origin.x = tagXOffset
tagXOffset += tagPadding + subview.frame.width
}
}

Get frame of the title Label inside the UIButton swift

I am trying to create a UIButton that contains a UIImage and text. So far I have managed to do that but I can't make it look pretty because the button width is half of the screen size (and this means the proportions are not right on big screens when I set the image)
Is there a way to position the Image exactly before the text Label in the button? Now I am doing this:
economicsButton.imageEdgeInsets = UIEdgeInsets(top: 15,left: 10,bottom: 15,right: 30)
economicsButton.titleEdgeInsets = UIEdgeInsets(top: 0,left: 0,bottom: 0,right: 0)
Try this
let somespace: CGFloat = 10
self.economicsButton.setImage(UIImage(named: "cross"), forState: UIControlState.Normal)
self.economicsButton.imageEdgeInsets = UIEdgeInsetsMake(0, self.economicsButton.frame.size.width - somespace , 0, 0)
print(self.economicsButton.imageView?.frame)
self.economicsButton.titleEdgeInsets = UIEdgeInsetsMake(0,(self.economicsButton.imageView?.frame.width)! + somespace, 0, 10 )
check the original answer Align image on right side

How do you add an outline to text programmatically?

I have two apps, one with a UILabel and one using SpriteKit with a SKLabelNode. I'd like to add a black outline around the white text.
I can't find any outline or border properties or anything like that within swift. I've tried just creating new labels with slightly bigger, black font behind them but that didn't look right at all.
Here is my game with SpriteKit
title.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
title.text = "Tap to start!"
title.fontName = "Arial"
title.zPosition = 10
title.fontSize = 50
self.addChild(title)
And here is my other one that uses UILables (It's within a red rectangle so it's easier to see)
let rectangle = UIView(frame:CGRect(x: self.view.frame.size.width / 2 - 150, y: self.view.frame.size.height / 2 - 75, width: 300, height: 150))
rectangle.backgroundColor = UIColor.red
self.view.addSubview(rectangle)
let label = UILabel(frame: CGRect(x: rectangle.frame.size.width / 2 - 75, y: rectangle.frame.size.height / 2 - 10, width: 150, height: 20))
label.textAlignment = .center
label.text = "Tap to Start!"
label.textColor = UIColor.white
label.font = UIFont(name: "Arial", size: 20)
rectangle.addSubview(label)
How do I outline these labels?
For SKLabelNode's, there is no 'easy' way of outlining text. There is however a 'hack'. It involves adding 8 additional duplicate nodes around the label that are coloured black (or whatever colour you want the outline to be), have a zPosition less than the original and otherwise, be identical.
It's important that each label has the same font size as the original.
Your attempt at doing this involved increasing the font size of your one outline copy label. As you said in the question, it doesn't work. You have to use multiple labels so the effect appears seamlessly.
You would place one copy directly above the original, one directly below, one to the left, one to the right, then one on each corner (top right, top left, bottom right, and bottom left). This gives the effect of an outline, however is not efficient and should be taken with a grain of salt.
Note: Keep the amount shifted from the original consistent for all the copies, so the 'outline' is evenly sized.
This should work for UILabels too, however I think there might be an easier way to do this with UILabels.

How do I set a layout margin for a text field in swift?

I am attempting to create margins in my text field so that when I go to type, the text isn't pressed so tightly against the edge.
I tried using this code (above viewDidLoad)
var insets = UIEdgeInsetsMake(10, 10, 10, 10)
Then putting this in my viewDidLoad()
textField.layoutMargins = insets
I ran the program and it still looked like there were no margins. How do I implement margins in a text field in Swift?
Subclass UITextField and implement textRectForBounds:. The simplest strategy is to call super, get the resulting rect, inset it as desired, and return it.
Here's an example result; note that the start and end of the text have considerable white space at the margin (of course the exact amount is up to you):
By creating new UIView with the right(your) values, you can set the padding in UITextField
textField.leftView = UIView(frame: CGRect(x: 30, y: 30, width: 100, height: 100))
textField.leftViewMode = UITextFieldViewMode.Always

Resources