I'm running into a weird issue with my code and I hope someone else has better ideas on how to handle this.
**Summary of what I want to achieve: **
I have an editor that looks like this:
On the right side I have an inspector panel where I can manually change the frame of the currently selected view (which sits inside another NSView that's the document view of an NSScrollView).
**Summary of implementation: **
The main view inside the NSScrollView doesn't directly use autolayout, because I need to be able to set the frame manually, I leave the translatesAutoresizingMaskIntoConstraints property to true (default value) for all the subviews inside the NSScrollView. So I end up with constraints automatically created when I set the frame.
The problem:
When I set the frame, to let's say (1, 0, 100, 100) for some reason the autolayout engine will take in account the magnification value of the NSScrollView and will readjust the frame, so the final frame might end up looking like (1.74, 0, 100, 100). While I do understand this, the question is, can I disable this behavior? Is it possible to have frame value increments of 1.0 while making sure Autolayout doesn't screw the final frame regardless of the NSScrollView magnification value?
Thank you!
Autolayout views inside a magnified NSScrollView can give very strange values at times, and I haven't really figured out when and why.
I have approached a similar problem by adjusting the views' position manually according to magnification of superview, or by subclassing them and writing a method for them to take the magnification into account.
So, for example:
CGFloat factor = 1 / magnification;
element.frame = NSRectMake(x * factor, y * factor, ...);
Hope this helps.
Related
I'm getting very confused with traits and constraints.
I have a view that needs to be in a 1:1 ratio, and it needs to have it's size dependent on the larger of either the screen width or screen height, at runtime, and with all device orientations.
On top of that, I need to move it down from the centre of the screen, by an amount that is calculated at runtime depending on orientation and iPhone vs iPad.
Is this possible or is it better to do all this in code and remove all AutoLayout constraints from this view?
I've read this: Aspect ratio constraint relative to the screen width
And also this: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/Size-ClassSpecificLayout.html
But I still can't seem to get it to do what I want.
Can someone please clarify what I need to do?
Thanks
You need to add at least these 2 constraints to your UIView in storyboard.
1st will be for the aspect ration and the 2nd will be for the width of the UIView with a constant of a value.
You will drug an outlet of the 2nd contraint to your UIViewController class named widthConstraint
On the delegate function ViewWillAppear() you will add this code that computes the max side and it adds that value to the contraint constant.
UIView.animate(withDuration: 0.0) {
let largestOfScreenSide = max(UIScreen.main.bounds.width, UIScreen.main.bounds.height)
self.widthConstraint.constant = largestOfScreenSide
self.view.layoutIfNeeded()
}
I have a view and inside it I have a UILabel. I want the view to change its width according to the length of the text.
What I tried is to first perform SizeToFit() on my UILabel, hoping this will change its frame, and then put the labels frame into the view's frame.
This doesn't work, I guess because of auto layout restrictions.
This is the code of what I tried:
private func example(){
self.labelInsideView.sizeToFit()
viewThatContainsLabel.frame = labelInsideView.frame
self.layoutIfNeeded()
}
It seems pretty obvious that this won't work, because I am pretty sure size to fit does not change the frame. Is there any way to achieve this?
Make an IBOutlet of your UIView's width constraint and then simply change the width constraint constant to the width of UILabel
self.labelInsideView.sizeToFit()
self.widthConstraintOfView.constant = labelInsideView.frame.size.width
self.layoutIfNeeded()
Why are you even using this function. Your view should have constraints.
Leading = Top = Bottom = 8 and Trailing >= 20
Your label should have all the constraints equal to 5.
Now try and add any text in your Label and you'll get updated frame of the view without using this function. Just try this once.
I subclass UISlider in order to make the thumb smaller, and I want to override minimumValueImageRectForBounds and maximumValueImageRectForBounds to make their width 2px less. So my code is
- (CGRect)minimumValueImageRectForBounds:(CGRect)bounds
{
CGRect stdRect = [super minimumValueImageRectForBounds:bounds];
return CGRectMake(stdRect.origin.x + 2, stdRect.origin.y, stdRect.size.width - 2, stdRect.size.height);
}
The point is that stdRect is empty rectangle (0, 0, 0, 0).
Morover, if I explicitly set some rectangle like that
- (CGRect)minimumValueImageRectForBounds:(CGRect)bounds
{
return CGRectMake(2, 0, 40, 8);
}
It doesn't affect minimum value image position at all.
Any ideas?
Haha, I just figured it out. I wasn't reading closely enough.
Setting the minimumValueImage is not the same as calling setMinimumTrackImage:forState:. The minimumValueImage is an image that gets displayed to the left of the slider and is independent what's going on in the slider. Overriding minimumValueImageRectForBounds changes the dimensions of this image (the default size being 0pt wide) and the sliders frame is made less wide and shifted to the right as a result. As I understand it, there isn't a way to modify the rectangle of the minimumTrackImage (such as to make it extend to the right of the thumb image); it is only possible to change the image.
I haven't really figured out the point of allowing you to set the minimumValueImage. It seems like you could accomplish the same thing by changing the size of the slider and adding separate UIImageViews to the side of the slider. Who knows.
Note that everything I've said here applies in the same way for the maximumValueImage and setMaximumTrackImage:forState methods.
DO NOT DO THIS. This is an old, incorrect answer, by past me. I hate that guy.
--
As a general rule, you probably don't want to subclass the Apple standard UI controls. You might want to build it up and set those properties yourself instead (not sure if they are settable, but seems like they should be).
I have an UIView that can grow dynamically in width. The view also have subviews located inside it's bounds.
Default behavior seems to be that when the view's frame grows along the x axis, increasing frame.size.width, it always grows at the right edge, keeping the subviews fixed as if there were a fix left margin. However, when I want to expand the view on the left edge this doesn't work because of this behavior. In this case I want it to behave in a mirrored way, as if there were a fix right margin. I could of course "manually" move all subviews so it looks like that is the case, but that seems really awkward since there could be plenty of them.
So I guess the question really is if there is a way to shift a views bounds relative to it's subviews? Is maybe autoresizingMask the way to do this?
Thanks.
Maybe you should take a look at the AutoresizingMask property of a UIView subclass :-)
For example, if you have a UILabel called labelVideoTitle, you could set a mask like this :
[ labelVideoTitle setAutoresizingMask:UIViewAutoresizingFlexibleWidth ];
You can by the way add 2 mask at once like :
[ labelVideoTitle setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight ];
Good Luck !
Edit : To increase the parent view frame size at the left edge, you could change too its X position to the left to give the impression wanted ^^ For example if you add 10 pt to the width, try modifying the X origin -10 pt :-)
In interface builder, you can graphically indicate in the CMD-3 (little ruler icon) Size Inspector what each element in your view should do when the parent view is resized: you can indicate which borders (top, left, right, bottom), the given element should "stick to" when the parent view is resized. You can also indicate whether the given element should itself resize (in either width or height) or stay the same size. Underneath the hood, this sets the autoresize mask for the UIView element you're editing, but especially for making an element stick to a particular border, Interface Builder is the way to go.
IB Size Inspector also has a neat little animation that shows you the effect on a hypothetical element (little red square) during a resize, given your settings to the left.
I have a custom table cell which contains a number of UILabels. At runtime, I am adjusting the height of the labels to fit their contents using sizeWithFont:constrainedToSize:lineBreakMode: and repositioning them accordingly. The last label in the cell contains a large amount of text, causing it to wrap, and I'm having a very odd problem. Although the sizeWithFont call returns the correct size, and I'm setting the UILabel's frame to that height, it draws a couple of lines short. This screenshot illustrates what I'm talking about:
In this example, the height of the full block of text should be 90 (as checked in Interface Builder), and that's what returns from sizeWithFont. It's also the height that the UILabel's frame is set to, which I have verified by logging and also by stopping execution and inspecting the value. However, as you can see, it's clearly not drawing the full 90 pixels high, although it's correctly allocating the space for it (the thin black line above 'Edited' is the table cell border). I'm completely perplexed. If anyone can offer some insight as to why it's behaving this way, I would be very grateful.
At last, a solution!
Turns out that the cell does layout twice -- once during heightForRowAtIndexPath, which is where I tweak all the heights of the subviews and the cell, and later during some untraceable transaction originating in __CFRunLoopDoObservers. How did I trace this? I added a layoutSubviews override to my custom table view cell class so I could breakpoint it.
During the second pass, the last UILabel subview was getting set to a shorter height than I set it to, probably in accordance with some arcane autoresizing rules. (Yes, I tried tweaking all of those settings first, with no success.) As it turns out, merely doing nothing in layoutSubviews disabled this framework behavior, allowing me to completely control how my views draw.
With iOS 8 it doesn't work anymore like this. Implementing layoutSubviews alone doesn't do the trick, because the layout of subviews have already changed when the method is called.
I have found 2 solutions:
adding NSLayoutConstraint to layout the subviews programmatically
implementing subview's layoutSubviews and change the frame
An example für solution 2:
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect frame = self.frame;
frame.size.height = 39.f;
self.frame = frame;
}
I've fought with similar problems. It was to do with other properties being set in previous incarnations of the cell. To find it / prove it I changed the reuseidentifer for the offending cell to make sure it was a unique cell.