I wanted a UILabel with icon.
So i created a utility which will create label, icon and deal with constraints for spacing between them etc.
Now when i use this component in my client code, i again need to create a UILabel in storyboard.
#IBOutlet weak var stateLabel: iconLabel!
...
stateLabel.style = withIcon
and
utility has prototype code:
struct style {
static let withIcon = style(
iconName = x.png,
textColor: white
}
class iconLabel {
createLabel() {
label = UILabel()
addSubview(label)
//deal with constraints
}
createIcon() {
icon = UIImageView
addSubview(icon)
//deal with constraints
}
}
So problem is:
1)I have to have label in my client + in my utility. How to avoid this.
2)If i delete label from my client file, then that label has constraints with other labels in storyboard and i dont understand if i remove it, how can i use my utility.
3)If i dont recreate label in utility, then how can i set constraints between label and icon.
4) If i dont create utility, then every client has to have UIImageView for icon and code for constraints betweein icon and label. So i wanted to create a utility.
Any hints ?
Thanks.
Have a look at this basic tutorial by apple for creating custom views:
Custom controls
Related
I am trying to create a UIView in a library project and use it in my application. I have added auto layout constraints as follows:
But it produces the following result:
The labels have numOfLines as 0 but still, empty labels are displaying empty space.
I have only given the height of the white view in the center (56px)
Edit:
I am using the view from library as following:
One solution with Storyboard, where UILabel heights are dynamic based on its's text,
Or,
you can try using NSLayoutConstraint for the UILabel hights, for the ones you want to hide when the value is not there for the label,
#IBOutlet weak var errorLabelHeight: NSLayoutConstraint!
then,
if errorLabel.text.isEmpty {
errorLabelHeight.isActive = true
errorLabelHeight.constant = 0
} else {
errorLabelHeight.active = false
//works as usual
}
view.layoutIfNeeded()
I have customised UI button and create some properties with IBInspectable. However, I also need the same property for selected or highlighted state and can be inspected in Interface Builder. I want to know if it can be achieved?
Here is the customized button I created
#IBDesignable
class ImageLabelButton: UIButton{
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
let buttonImgView = UIImageView()
let buttonLabel = UILabel()
// let stackView = UIStackView()
// Override property observors
#IBInspectable
var textColor:UIColor? {
get {
return buttonLabel.textColor
}
set(newValue) {
self.buttonLabel.textColor = newValue
}
}
}
I want to create a IBInspectable property for other states as well. Can it be done? Thanks!
Short answer? No. Interface Builder cannot "process" code.
I have a need to know when my app is in portrait or landscape orientation (various slider controls are on the bottom or right depending on this).
Can I use IB for this? Not if I need to know on an iPad... it's size class is (wR hR) unless the slide out or split screen is there. I can "design" something for each orientation, but even Apple - WWDC'16, Making Apps Adaptive, Part 2 - ended up putting code into viewWillLayoutSubviews() for this.
Put a UIButton on your storyboard. Can you process a tap? Put a UISlider on the storyboard. Can you pan it left or right?
You are asking a design time tool to process run time actions.
So again, you can't make certain button states IBDesignable.
Is it possible to define a variable, and set a constraints constant value to that variable?
Thereby making it possible to change many constraints by just changing the variable. I think I saw someone do this directly from interface builder ones?
EDIT:
There is a constraint between each label. I need a method to change all of these constraints, so they get a the same value. Is this possible?
If I use a outlet collection, I will have to iterate through all the constraints, and change the value for each. I'm looking for a method like this:
// SEUDO!!
lineSeperationWidth = 31 // changes all 4 constraints.
The NSLayoutContraints are all separate objects, and Xcode provides no way to set the constant value of multiple constraints with the same variable. The closest you can get is to use an #IBOutlet collection and then use the didSet property observer for your variable holding the common space value to update each of the constraints in the collection.
Here is an example I created which spaces out the labels based upon a random value that I set to the space property each time the Go button is pressed:
class ViewController: UIViewController {
#IBOutlet var spacers: [NSLayoutConstraint]!
var space: CGFloat = 20.0 {
didSet {
spacers.forEach { $0.constant = space }
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func spaceOut(sender: AnyObject) {
// set space to a random value
space = CGFloat(arc4random_uniform(30)) + 20.0
}
}
Each of the vertical space constraints between the labels is connected to the #IBOutlet collection spacers. Here is what the Connections Inspector shows:
Here it is in action:
Yes it is! You can use the document outline view to find the constraint you want to use as a variable. Once you have it, CTRL + Drag from the constraint in the document outline view to your code to make the outlet. Then you can change the constraints in code by using self.constraint.constant = 31.
To space all the views out equally, you can put UILayoutGuides in between each of the labels and constrain all their heights to be equal. Then you can change the height of one of the layout guides and they will all change to match it.
I am going through the Stanford Winter 2015 Swift/iOS course and while doing the assignments I run into a behavior I'd like to change.
I use Autolayout as described in the videos (making the display pin to leading and trailing view edges) and the Calculator app "Display" UILabel is fine with an initial value of 0 and whenever the value used to set it (a String) is non-nil and non "".
If it is either nil or "", the entire UILabel disappears. What I am trying to do is to "clear" the display whenever there is no value to display or an incorrect calculation resulted in nil.
Any tips on who to deal with this in general? "Clearing" a UILabel without changing it's on-screen dimensions?
Edit (thanks Rob)
The UILabel has the following constraints
1. Option-click drag-left to containing UIView, selected "leading" something (on commute to work can't check yet for exact wording.
2. Same method as (1) except that the drag is to the right edge and selecting "trailing"
3. Option click-drag up to top of view, select "vertical" menu option.
4. Same as (3) except that drag is to a UIButton underneath the UILabel on the GUI.
With those settings, the label when it contains a number is always visible and (if understand, will color it to verify) stretches across the screen even if the text doesn't.
The layout looks correct in profile and landscape as long as content of UILabel is not empty. If empty, it seems to "shrink to fit" so much that the buttons below get moved up towards the top.
I'm a C++ dev since mid 90s but I have little UI experience and no more than a couple weeks experience in iOS/Swift development.
Thanks!
You can always give the UILabel a min width and min height or constraints that holds the left and right side of the label. That should keep the label from changing it's dimensions to zero.
Use a custom UILabel class assigned in Interface Builder >> Identity inspector >> Custom Class >> Class to override UILabel intrinsic content size.
No need to create any superfluous auto-layout constraints.
Swift:
class UILabelNonCompressible: UILabel
{
private static let NonCompressibleInvisibleContent = " "
override var intrinsicContentSize: CGSize
{
if /* zero-width */ text == nil ? true : text!.isEmpty
{
// prefer mirror-and-calculate over modify-calculate-restore due to KVO
let doppelganger = createCopy()
// calculate for any non-zero -height content
doppelganger.text = UILabelNonCompressible.NonCompressibleInvisibleContent
// override
return doppelganger.intrinsicContentSize
}
else
{
return super.intrinsicContentSize
}
}
}
You will also need "How do copy for UILabel?":
extension UILabel
{
func createCopy() -> UILabel
{
let archivedData = NSKeyedArchiver.archivedData(withRootObject: self)
return NSKeyedUnarchiver.unarchiveObject(with: archivedData) as! UILabel
}
}
when I update segmented control text, the interface (segment's width) changed and cut some letters.
[segmentedcontoll setTitle:#"test" forSegmentAtIndex:1];
segmentedcontoll.apportionsSegmentWidthsByContent = YES;
How can I solve this ?
EDIT:
It looks like your content has outgrown the dimensions of the standard UISegmentedControl.
If you are okay with smaller font, it's possible to set the entire control to have a smaller font point size, seen here.
Another option is to configure the segments the other supported way.. With images. It's a little bit of a hack, but you can create images on the fly with the UIView Snapshotting API of views/labels configured however you want and set images for each segment instead of using text. This would allow you to create 2 line labels with fixed widths and set images for each section to be images generated from the label as the content changes. More work, but you would still be using the standard class.
The last option, which might work the best for you, is to create some other custom control that does what you would like. After all, UISegmentedControl really is just a nice button container. And it does somewhat seem like you are using the control in a non-standard way - both as a control and an input form section.
Others have gone this route before and created alternatives that you can use.
You can create a separate class as below,
class CustomSegmentedControl: UISegmentedControl {
//code for creating multi line
override func didMoveToSuperview()
{
for segment in subviews
{
for subview in segment.subviews
{
if let segmentLabel = subview as? UILabel
{
segmentLabel.numberOfLines = 0 //just change here the number of lines and check it.
}
}
}
}
}
and create an outlet in your viewcontroller as,
// Initialize
let items = ["Purple", "Green", "New Segment"]
let customSC = CustomSegmentedControl(items: items)
use customSC and do what ever you want to do, similar to segmentedControl object.