Can you reference a view in AutoLayout in Storyboard?
In the "constant" field, I want to enter something like myCustomView.width + 14.
Is there a way to get this dependency?
You need to create an #IBOutlet on your MyCustomView class definition like this
#IBOutlet weak var platzHalterLogoTrailingConstraint: NSLayoutConstraint!
then tie that constraint, using the interface builder.
You will define an initial value on your storyboard, and then programatically change it when you need to.
override func viewDidAppear() {
super.viewDidAppear()
platzHalterLogoTrailingConstraint.constant = calculateTrailingConstant()
}
Related
I've 15 Labels in my Storyboard they are just texts, also set from storyboard, What I want to do is to style them, but programitically, Therefore I need to create 15 IBOutlets in my ViewController, I wonder if there is any other way of doing that, without 15 IBOutlets,if it's possible to create 1 IBOutlet and attach all of them to that one? because creating 15 of them is kinda stressing...
You can do this with Outlet Collections instead of an IBOutlet for all the labels you want to group together:
One way to do it is to ctrl+drag from your storyboard to your editor and select outlet collection
This will create #IBOutlet weak var labelCollection: UILabel! in your code
This works fine but then you need to add an additional check for the type when looping:
#IBOutlet weak var labelCollection: UILabel!
func setCustomLayout()
{
for label in labelCollection2.subviews
{
if let label = label as? UILabel
{
// do your custom set up here
}
}
}
What I like to do is to create the specific outlet collection in code first if I way to track the same type like so:
#IBOutlet var labelCollection: [UILabel]!
The I drag from the editor to the storyboard
Then I can work with it as follows
#IBOutlet var labelCollection: [UILabel]!
func setCustomLayout()
{
for label in labelCollection
{
// do your customization here
}
}
Then you can loop through the UIViews inside the IBOutletCollection and do the needful
Please give me a hint how to add IBOUTLET variables to the ViewControllerSettings class. TableViewController is embedded on Container View.
You need to control drag and drop it inside the ViewControllerSettings class's definition.
class ViewControllerSettings: UITableViewControntroller {
#IBOutlet weak var tableView: UITableView!
//...
}
Note: Make sure the class for the UITableViewController is set to ViewControllerSettings in your storyboard.
#IBOutlet weak var tableView: UITableView!
control Drag the table view to the above #IBOutlet
From your screenshot, it seems that you have not assigned the ViewControllerSettings for UITableViewController inside storyboard.
Inside storyboard, select added UITableViewController, got to Identity Inspector and set Class as ViewControllerSettings.
Also do connect outlets inside your class in .swift file.
I'm new to Swift/macOS dev, plenty of dev experience otherwise though. Just trying to make something rudimentary.
Here's my app storyboard:
I'm trying to get:
the Touch Bar slider to change when the slider on the main window changes
vice versa
update the Touch Bar Label button with the Int value of the slider.
Q) How do I achieve this?
Note: The main window slider control is wired up and working when I manipulate it e.g.
#IBOutlet weak var mySlider: NSSlider!
#IBAction func mySlider_Changed(_ sender: NSSlider) {
//... stuff happens here.
}
You'll want your view controller to have some explicit model/state of what the value of these sliders have. e.g.
class ViewController : NSViewController {
var value: Double
}
Then you can connect the sliders and textfield to update or display this value.
Approach 1: Target/Action/SetValue
This follows the use of explicit IBActions that you had started. In response to that action, we'll pull the doubleValue from the slider and update the ViewController's model from that:
#IBAction func sliderValueChanged(_ sender: NSSlider) {
value = sender.doubleValue
}
The second piece is updating everything to reflect that new value. With Swift, we can just use the didSet observer on the ViewController's value property to know when it changes and update all of the controls, e.g:
#IBOutlet weak var touchBarSlider: NSSlider!
#IBOutlet weak var windowSlider: NSSlider!
#IBOutlet weak var windowTextField: NSTextField!
var value: Double {
didSet {
touchBarSlider.doubleValue = value
windowSlider.doubleValue = value
windowTextField.doubleValue = value
}
}
And that's it. You can add a number formatter to the textfield so it nicely displays the value, which you can do in Interface Builder or programmatically. And any other time you change the value, all of the controls will still get updated since they are updated in the didSet observer instead of just the slider action methods.
Approach 2: Bindings
Bindings can eliminate a lot of this boiler plate code when it comes to connecting model data to your views.
With bindings you can get rid of the outlets and the action methods, and have the only thing left in the view controller be:
class ViewController: NSViewController {
#objc dynamic var value: Double
}
The #objc dynamic makes the property be KVO compliant, which is required when using bindings.
The other piece is establishing bindings from the controls to our ViewController's value property. For all of the controls this is done by through the bindings inspector pane, binding the 'Value' of the control to the View Controller's value key path:
And that's it. Again, you could add a number formatter to the textfield, and any other changes to the value property will still update your controls since it will trigger the bindings to it. (you can also still use the didSet observer for value to make other changes that you can't do with bindings)
I am trying to add a custom view programmatically to my view controller. I used this snippet of code with no success of it appearing in front of my main view controller.
var DynamicView = CustomFilter()
DynamicView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(DynamicView)
CustomFilter Class:
import UIKit
class CustomFilter:
UIView {
#IBOutlet weak var party: UIButton!
#IBOutlet weak var outdoors: UIButton!
#IBOutlet weak var sports: UIButton!
#IBOutlet weak var diner: UIButton!
#IBOutlet weak var music: UIButton!
#IBOutlet weak var gaming: UIButton!
}
The custom filter is connected to a xib file.
Xib File:
Is there a possibility that the custom view maybe out of placed? I'm not sure what I'm doing wrong.
In order to use a view we designed in a xib, we most load from the xib.
if
let bundle = NSBundle(forClass: CustomFilter.self),
let nib = bundle.loadNibNamed("<#Xib File Name#>", owner: self, options: nil),
let dynamicView = nib.first as? CustomFilter {
self.view.addSubview(dynamicView)
}
An alternative approach would be to write your CustomFilter's init to load the view from the xib itself.
More clearly, the problem you're having is that none of your CustomFilter's initializers are going to care about the xib file you made unless you write them and tell them to care about it. Your current code is returning a 0x0 view with probably a white or clear background. If you modified your current code to set the CustomFilter's frame to something other than 0x0 size and set the background color to something like UIColor.greenColor(), you'd see it clear as day.
Also, you could use Xcode's visual debugger to find it.
It's probably zero height and zero width, and may be off-screen also.
You need to give it height and width constraints and x and y position constraints.
You should probably also use CustomFilter(frame: someFrameRect), since as I recall initWithFrame is the designated initializer for UIView.
As an aside, variable names should start with a lower-case letter, so DynamicView should be dynamicView
I'm trying to replicate the Stanford Matchismo game from "Developing ios7 apps for iphone and ipad" in iTunesU in Swift.
On page 77 of the 3rd lecture slides, it shows using an IBOutletCollection which isn't an option on Swift. The Swift doc example shows one example that has an array of IBOutlet, but I can't figure out how to make Interface Builder connect multiple outlets to the same IBOutlet/IBOutlet Array.
Has anyone figured out how to do this yet?
I know that I can create 12 outlets and deal with it that way, but I'd like to make this work as closely as possible to the example in the lecture slides.
Update: This works properly in Xcode now - "Outlet Collection" is one of the connection options in Interface Builder, which creates something that looks like:
#IBOutlet var labelCollection: [UILabel]!
While we're waiting for a fix, you can approximate this using a computed property. Let's say my view has five UILabels that I want in a collection. I still have to declare each one, but then I also declare a computed property that collects them:
class MyViewController {
#IBOutlet var label1 : UILabel
#IBOutlet var label2 : UILabel
#IBOutlet var label3 : UILabel
#IBOutlet var label4 : UILabel
#IBOutlet var label5 : UILabel
var labels: UILabel![] { return [label1, label2, label3, label4, label5] }
Kind of annoying, but from then on we can treat the labels property as if it were an IBOutletCollection, and won't have to change the rest of our code once the bug is fixed:
override func viewDidLoad() {
super.viewDidLoad()
for (index, item) in enumerate(self.labels) {
item.text = "Label #\(index)"
}
}
Use:
#IBOutlet var lineFields: [UITextField]!
Then control-drag from UITextField elements to lineFields in order.
#IBOutlet var buttons : [UIView]!
then drag it from the connections inspector in the interface builder or whatever metod you usually use for that
EDIT
This was fixed in a later Beta release of Swift - there's now in
IBCollection option in the interface builder.
For early Beta releases of Swift:
I came across the same problem: in the release notes of Beta 2 you find the following statement:
Interface Builder does not support declaring outlet collections in Swift classes
I solved this the following way (easy to customize):
class CardGameViewController: UIViewController {
#lazy var cardButtons : UIButton[] = {
var tempBtn: UIButton[] = []
for v:AnyObject in self.view.subviews {
if v is UIButton {
tempBtn.append(v as UIButton)
}
}
return tempBtn
}()
...
Basically, it loops through all the subviews and checks if one is a UIButton. In that case it gets added to a temporary array. This temporary array is then used to lazy instantiate the cardButtons array. For all details, check: Matchismo: Objective-C to Swift
Follow steps to create an array of outlets and connect it with IB Elements:
Create an array of IBOutlets
Add multiple UIElements (Views) in your Storyboard ViewController interface (As shown in below snapshot)
Select ViewController (In storyboard) and open connection inspector
There is option 'Outlet Collections' in connection inspector (You will see an array of outlets there)
Connect if with your interface elements
-
class ViewController2: UIViewController {
#IBOutlet var collection:[UIView]!
override func viewDidLoad() {
super.viewDidLoad()
}
}
I got this working in Xcode seed 3 using this syntax
#IBOutlet strong var views: NSArray?
See my discussion here: https://stackoverflow.com/a/24686602/341994
What #machine said seems to be the current state (XCode 7.1) with iOS 9 bindings.
The key is to drag them all in order.
Use the first item to control+drag into the controller code and then change the Outlet type to collection. After the from the controller code file drag the outlet point onto each of the screen controls one by one in order (as #machine says)