xsheru
xsheru

Reputation: 497

Custom View size in iOS not dynamic

I just have started my first app on iOS a week ago. I have created a custom view to use it in my app using AppleDeveloperWebsite Custom Rating Control Tutorial.

Now I have chosen iPhone7 device in storyboard and I run this on iPhone 7 emulator it works perfectly but when I run it on iPhone 5 emulator (size of screen changes) my custom views extend beyond the screen. My all other controls sizes resize as I set constraints but only my custom view sizes get messed up.

Please Help

import UIKit

@IBDesignable class xRadioButtonView: UIView {
var button: UIButton!
var label: UILabel!

@IBInspectable var text: String? {
    didSet{
        label.text = text
    }
}

//Properties
var isSelected = 0

//Initialization
override init(frame: CGRect){
    super.init(frame: frame)
    addSubviews()
}

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)!
    addSubviews()
}

func addSubviews() {
    self.backgroundColor = UIColor.white

    let xWidth = bounds.size.width
    let xHeight = bounds.size.height

    let tap = UITapGestureRecognizer(target: self, action: #selector(xRadioButtonView.radioButtonTextTapped))

    button = UIButton(frame: CGRect(x: 1, y: 1, width: xWidth - 2, height: xHeight - 4))
    button.backgroundColor = UIColor.white
    button.addTarget(self, action: #selector(xRadioButtonView.radioButtonTapped(button:)), for: .touchDown)

    addSubview(button)

    label = UILabel(frame: CGRect(x: 1, y: 1, width: xWidth - 2, height: xHeight - 2))
    label.textColor = UIColor.init(hex: "#D5D5D5")
    //label.font = UIFont.init(name: label.font.fontName, size: 25)
    //label.font = label.font.withSize(25)
    label.font = UIFont.boldSystemFont(ofSize: 25)
    label.textAlignment = NSTextAlignment.center
    label.isUserInteractionEnabled = true
    label.addGestureRecognizer(tap)

    addSubview(label)
}

override func layoutSubviews() {
    // Set the button's width and height to a square the size of the frame's height.

}

override func prepareForInterfaceBuilder() {
    super.prepareForInterfaceBuilder()
    label.text = "xRBV"
}

func radioButtonTapped(button: UIButton) {
    if isSelected == 0 {
        isSelected = 1
        self.backgroundColor = UIColor.init(hex: "#00BFA5")
        label.textColor = UIColor.init(hex: "#00BFA5")
    } else {
        isSelected = 0
        self.backgroundColor = UIColor.white
        label.textColor = UIColor.init(hex: "#D5D5D5")
    }
}

func radioButtonTextTapped(sender: UITapGestureRecognizer){
    if isSelected == 0 {
        isSelected = 1
        self.backgroundColor = UIColor.init(hex: "#00BFA5")
        label.textColor = UIColor.init(hex: "#00BFA5")
    } else {
        isSelected = 0
        self.backgroundColor = UIColor.white
        label.textColor = UIColor.init(hex: "#D5D5D5")
    }
}
}

As you can see PG button should finish where green color finishes but white color button is extended beyond the screen

Upvotes: 0

Views: 753

Answers (2)

Josh Homann
Josh Homann

Reputation: 16327

You either need to set the frame in layoutSubViews or you need to implement autolayout in code:

    button = UIButton(frame: CGRect(x: 1, y: 1, width: xWidth - 2, height: xHeight - 4))
    button.backgroundColor = UIColor.white
    button.addTarget(self, action: #selector(xRadioButtonView.radioButtonTapped(button:)), for: .touchDown)
    addSubview(button)
    button.translatesAutoresizingMaskIntoConstraints = false
    let attributes: [NSLayoutAttribute] = [.top, .bottom, .leading, .trailing]
    let constants = [2, 2, 10, 10]  // 2 from top and bottom, 10 from leading and trailing
    NSLayoutConstraint.activate(attributes.enumerated().map { NSLayoutConstraint(item: button, attribute: $1, relatedBy: .equal, toItem: button.superview, attribute: $1, multiplier: 1, constant: constants[$0]) })

The example uses the old way because your constraints are uniform, but if you have something more complicated its often simpler to use NSLayoutAnchor as of iOS 9.

EDIT: here is the code for tuples if anyone is interested:

    button.translatesAutoresizingMaskIntoConstraints = false
    let attributes: [(NSLayoutAttribute, CGFloat)] = [(.top, 2), (.bottom, 2), (.leading, 12), (.trailing, 12)]
    NSLayoutConstraint.activate(attributes.map { NSLayoutConstraint(item: button, attribute: $0.0, relatedBy: .equal, toItem: button.superview, attribute: $0.0, multiplier: 1, constant: $0.1) })

Upvotes: 1

xsheru
xsheru

Reputation: 497

Thanks I wasn't even aware of this NSLayout yet. (HEHE 7 Days) Thanks to you I have a solution for my problem. Although I wanted different values for .top .bottom .leading .trailling

I used your code like this

 NSLayoutConstraint(item: button, attribute: .top, relatedBy: .equal, toItem: button.superview, attribute: .top, multiplier: 1, constant: 1).isActive = true

 NSLayoutConstraint(item: button, attribute: .bottom, relatedBy: .equal, toItem: button.superview, attribute: .bottom, multiplier: 1, constant: 4).isActive = true 

to all 4 sides. But is there a way to provide constant values as well like you have provided multiple attribute values?

Upvotes: 0

Related Questions