I'm getting mad at this code because it just shows the last view (in it's correct position) and it forgets to paint the others...
// Gets a stack of devices
let volumes = getDevices()
var index = 0
for device in volumes {
let volume = devicePrototype
// devicePrototype it's a custom view
volume?.setName(name: device.name) // setting the label for the name
volume?.setIcon(icon: device.icon) // setting the image for the icon
// Setting the position of the view (width = 600, height = 105)
// and all the views have the same size.
volume?.layer?.frame = CGRect(x: 0, y: index * 105, width: 600, height: 105)
// Adding the view to the NSClipView (Here's the problem :P)
devicesStack.addSubview(volume!)
index += 1
}
Can you help me find the problem please?
I think you are creating only one view (device) and modifying the same through the loop. You are referencing same devicePrototype object repeatedly and changing the properties only. Please check that.
Try to initialize a new object in each iteration,
let volume = DevicePrototype()
Related
I'm stuck at the Consolidation IV challenge from hackingwithswift.com.
Right now I'm trying to create a hangman game. I thought to place placeholder labels based on the length of the answer word. These placeholder labels would be placed inside a frame, which then would be placed in the center of the main view.
Unfortunately, the leading edge of the frame is placed centered. In my opinion, this is not a problem of constraints, but rather a problem of me creating the frame wrong.
My current code is
import UIKit
class ViewController: UIViewController {
var answer: String!
override func viewDidLoad() {
super.viewDidLoad()
// MARK: - declare all the labels here
let letterView = UIView()
letterView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(letterView)
// MARK: - set constraints to all labels, buttons etc.
NSLayoutConstraint.activate([
letterView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
letterView.centerXAnchor.constraint(equalTo: view.layoutMarginsGuide.centerXAnchor)
])
// MARK: - populate the letterView
// set the size of the placeholder
answer = "Atmosphäre"
let height = 60
let width = 25
// var width: Int
for placeholder in 0..<answer.count {
// create new label and give it a big font size
let placeholderLabel = UILabel()
placeholderLabel.text = "_"
placeholderLabel.font = UIFont.systemFont(ofSize: 36)
// calculate frame of this label
let frame = CGRect(x: placeholder * width, y: height, width: width, height: height)
placeholderLabel.frame = frame
// add label to the label view
letterView.addSubview(placeholderLabel)
}
}
}
The simulator screen looks just like this:
I already searched for answers on stackoverflow, but wasn't successful. I think I don't know what I'm exactly looking for.
The main problem, is that the letterView has no size, because no width or height constraints are applied to it.
To fix your code make the letterView big enough to contain the labels you've added as subviews by adding height and width constraints after the for loop:
for placeholder in 0..<answer.count {
...
}
NSLayoutConstraint.activate([
letterView.widthAnchor.constraint(equalToConstant: CGFloat(width * answer.count)),
letterView.heightAnchor.constraint(equalToConstant: CGFloat(height))
])
I'm not sure if you've covered this in your course yet, but a better way to go about this (which would take much less code), is to use a UIStackView as your letterView instead.
An extra thing to consider:
If you give the letterView a background color, you'll see that the labels are actually aligned outside of its bounds:
That's because you're setting each label's y position to be height, when it should probably be zero:
let frame = CGRect(x: placeholder * width, y: 0, width: width, height: height)
Correcting this places the labels within the bounds of the letterView:
i have created a subview with a lot sliders in it
var sliderArea = UIView()
sliderArea = UIView(frame: CGRect(x: 300, y: 400, width: 500, height: 100)
view.addSubview(sliderArea)
mySlider1 = setUpSlider(sliderNr: 1, ypos: 30)
mySlider2 = setUpSlider(sliderNr: 2, ypos: 30)
mySlider3 = setUpSlider(sliderNr: 3, ypos: 30)
sliderArea.addSubview(mySlider1)
sliderArea.addSubview(mySlider2)
sliderArea.addSubview(mySlider3)
i have a lot of subviews similar to the "sliderArea" to be able to change my sliders quickly while the layout adopts automatically
now i need to know where the absolute position of each slider is to place buttons on top of it. i need to have all this buttons inside an extra view on top of it all. any ideas? thank you
You should convert your slider frame to window's coordinate system using this method of CGRect:
https://developer.apple.com/documentation/uikit/uiview/1622504-convert
Just pass nil as second parameter and it will return you an absolute frame.
I'm experimenting with layout anchors, and I'm trying to replicate the following illustration: basically an outer view with an inner view. The inner view is anchored with constants to the outer view's bottom anchor and the leading anchor. This much is done.
However, I would then like to place a label inside the inner view, and be able to center the label/s in that view. Is this possible?
Ideally I could then go an alter the constants of the view later on, and the labels would still be correctly positioned.
Just some pointers in the right direction would be useful.
First create the view.
let innerView = UIView()
Then the labels.
let label1 = UILabel()
let label2 = UILabel()
Place the inner view inside the main view
view.addSubview(innerView)
After you add the innerView to the view.
You can provide a position and size to the inner view.
innerView.frame = CGRect(x: (self.view.frame.size.width) - (self.view.frame.size.width) + 10, y: (self.view.frame.size.height) - (self.view.frame.size.height) - 150, width: 500, height: 500)
Then add in the labels into the inner view
innerView.addSubview(label1)
innerView.addSubview(label2)
Then you can apply constraints to the labels inside the inner view and give it size
label1.frame = CGRect(x:100, y: 100, width: 200, height: 20)
label2.frame = CGRect(x:100, y: 100, width: 200, height: 20)
The quick pointer is to use UIStackView with
stack.addArrangedSubview(lbl)
distribution = fill
alignment = center
axis = vertical
You may also need to set lbl's alignment = center
Please don't mark as duplicate. Available threads haven't provided an answer. Behavior is iOS11 only.
Updating a project from Xcode 8 to Xcode 9, using now iOS11 but still Swift 3, I have the following experience:
print("UIScreen.main.bounds.width = \(UIScreen.main.bounds.width)")
print("self.view.frame.width = \(self.view.frame.width)")
let rect = CGRect(
x: 0,
y: 0,
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height * 0.25
)
prints:
UIScreen.main.bounds.width = 414.0
self.view.frame.width = 600.0
The view is supposed to be from screen edge to screen edge. Therefore I have used UIScreen.main.bounds.width. But the value far too small for the actual view controller size of self.view.frame.width.
Why is that? What am I missing? Help is very appreciated.
Physical device. Same behavior using UIScreen.main.bounds.width
I had a similar problem with incorrect fetching of UIScreen.main.bounds size. The size was less than a physical screen. The solution was to add the next line into info.plst:
Key:
Launch screen interface file base name
Type:
String
Value:
LaunchScreen
So check your info.plist to see if it contains this line. If it's not, just add it. Hope this helps.
screen shot of info.plst
In an NSTextView subclass I have created, I want to resize the height of the view to the height of the text within it. To execute this, I used apple's recommended procedure of counting lines within a text view:
private func countln() -> Int {
var nlines: Int
var index: Int
var range = NSRange()
let nGlyphs = lManager.numberOfGlyphs
for (nlines = 0, index = 0; index < nGlyphs; nlines++) {
lManager.lineFragmentRectForGlyphAtIndex(index, effectiveRange: &range)
index = NSMaxRange(range);
}
return nlines
}
This method works as expected and returns the correct number of lines in the text view. The issue lies in the resizing of the view, which I inserted into the delegate method that is called on text change:
func textDidChange(notification: NSNotification) {
let newHeight = CGFloat(28 * countln())
let ogHeight = self.frame.height
self.setFrameSize(NSSize(width: self.frame.width, height: newHeight))
self.setFrameOrigin(NSPoint(x: self.frame.origin.x, y: (self.frame.origin.y - self.frame.height) + ogHeight))
Swift.print(frame.height)
}
The setFrameSize variable function resizes the height of the view based not the number of lines in the view (multiplied by a constant that is more-or-less the height of each line of text). Everything works perfectly until immediately after the change of height is made, when the text view's height changes to an unanticipated incorrect height. I presume there is an issue with the frequent redrawing of the view in relation to the way I am resizing it. Any help on how to solve this issue of incorrect height resizing is greatly appreciated.
Thanks in advance.