I got auto-layout issue with custom UIView which render correctly in iphone XR
as
but wrong in iphone 7s(the "Reset" button is rendered out of bound)
and setting in storyboard with just a viewcontroller(UI), and no specified UIViewController class, for simplification i just want to debug the view so i do not associate any viewcontroller class to it.
p.s Safe-area has been used
and the custom class UI(FilterView) setting:
and the custom class UI(FilterView) code:
override init(frame: CGRect){
super.init(frame: frame)
setUpView()
}
//for IB
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
}
private func setUpView(){
Bundle.main.loadNibNamed(Constants.NIB_FILTER_VIEW_NAME, owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = []
contentView.translatesAutoresizingMaskIntoConstraints = true
}
Anyone knows what happened here?
You should not be creating the X button and "Sort by" label that way. What you are doing is replicating what a UINavigationController does. If you want that layout then put your view controller inside a UINavigationController and it will make it much much easier to add the X and the "Sort by" label.
Also by doing this you can add a rightBarItem to be your "reset" button. Again, you don't have to lay this out. UINavigationController does all that for you.
Related
I have a custom xib that has a horizontal stack view with 3 child views. I use this view in other xibs. When the user clicks on one of these child views, I scale it using:
view.transform = CGAffineTransform(scaleX: 2, y: 2)
When the view is scaled, it's actually meant to appear out of the parent view's bounds. However, when I click the child view, it scales it fine, but the view is cut off.
I've double checked that all of my views in the xib have clipsToBounds set to false, but it's still cutting it off.
Here is what my xib class looks like:
class CustomView: UIView {
#IBOutlet var contentView: UIView!
#IBOutlet weak var stackView: UIStackView!
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
func initialize() {
Bundle.main.loadNibNamed("CustomView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
... some other things that I've left out that aren't important ...
}
Is there something I'm missing? How do I make the child views not get clipped/cut off?
What about the view hosting the XIB? (CustomView in this case). Set clipsToBounds=false in your initialize. Otherwise, take a look at Xcode's view hierarchy debugger and see if there's another view that causing the clipping.
When the app run in Simulator it works but in Storyboard can’t see the preview, why?
Swift code:
Simulator and Storyboard:
Can’t see the custom view background color and when drag UIButton object into custom view doesn’t see the real position(x, y) and its background color.
In Android Studio when you add an object (custom view) in layout.xml file you can see the preview automatically, is it possible to do the same thing in Xcode?
TL;DR; Call in prepareForInterfaceBuilder
// Call The Custom Setup Here
override func prepareForInterfaceBuilder() {
setupView()
}
Calling in layoutSubviews also works, but is called multiples times in runtime, prepareForInterfaceBuilder is called only for Designables Changes, and only with this purpose.
Long Code:
#IBDesignable
class CustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
override func prepareForInterfaceBuilder() {
setupView()
}
func InitChildPosition() {
var i = 1
for _view in self.subviews {
if _view is UIButton {
_view.center.x = (_view.bounds.width / 2)
_view.center.y = (_view.bounds.height / 2)
}
if _view is UIButton && i == 2 {
_view.center.x = self.bounds.width - (_view.bounds.width / 2)
_view.center.y = self.bounds.height - (_view.bounds.height / 2)
_view.backgroundColor = UIColor.darkGray
}
i += 1
}
}
func setupView() {
self.backgroundColor = UIColor.black
InitChildPosition()
}
}
Call your CustomSetup() from init(frame:)
EDIT
Interface Builder uses init(frame:) to create and position views in storyboards.
You are modifying the subviews of CustomView in CustomSetup() which is called from the initializer. Your view does not have any button subviews in that case, because subviews can only be added after the view has been initialized.
You need to defer calling CustomSetup() to a later point, awakeFromNib() should work in your case.
Better yet, remove CustomSetup() completely and just style your UIButtons in the Interface Builder. You will get the same results without all the complexity.
After updating to Xcode 8.1 our storyboards and xib are as usual. If we create a new xib/view/storyboard we can't see the freshly added elements in the new view.
Both of these are xib files. The left one is created befor the update, the right one after. You cant see the button eventhough that it is on top of everything and has contrains to fill the view. It also has text content and no sized classes.
I know that there are alot questions which explain that this could be sized classes.
why storyboard ui elements not showing on UIViewController in xcode 6?
Storyboard UI Elements not displaying in editor
If we add new elements to it they are directly not visible. Also adding new ViewController to a "old" storyboard it does not show its content if we add elements to it.
What is going on here and how do i solve that?
For example, we have TopView (my CustomView):
TopView.xib, set TopView class in File's Owner
TopView.swift
import UIKit
#IBDesignable
class TopView: UIView {
//MARK:- IB Outlets
var contentView:UIView?
//MARK:- Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupThisView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupThisView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupThisView()
contentView?.prepareForInterfaceBuilder()
}
//MARK:- Lifecycle methods
private func setupThisView(){
guard let view = loadViewFromNib() else { return }
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
contentView = view
}
func loadViewFromNib() -> UIView? {
let nibName = String(describing: TopView.self)
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self,options: nil).first as? UIView
}
}
3.Add UIView in Storyboard and set the class of the view as TopView
If the TopView has not yet appeared in Storyboard then (this happens usually):
Enable Editor -> Automatically Refresh Views
Click on Editor -> Refresh All Views
Still?
Clean the project: ⌘ + K
Build the project ⌘ + B
Result in Storyboard:
p.s I just copy this one from my answer here: Custom view (xib) not visible on storyboard
1. Open the Xib/Storyboard
2. Select the file attributes tab (on the far left)
3. click the "opens in" property and change it from Xcode 8 to Xcode 7
Is it possible to create reusable stack views on the story board that can be used dynamically to be generated at a later time? Sort of a template/widget/component.
I am aware that I can do this with a class but if I am able to visually generate a set of components that can be re-used at a latter time I may be able to let our designers make changes to storyboards directly.
Yes -- you can do this with any UIView. There are many tutorials for this (e.g. http://onedayitwillmake.com/blog/2013/07/ios-creating-reusable-uiviews-with-storyboard/)
The basic idea is to drag one onto Storyboard or XIB, make a custom class for it, then implement the view's awakeFromNib to load it.
Yes.It is.
Create a empty xib and then add a stack view to it.
Then create a class which extends UIStackView.
class stackView: UIStackView {
var contentView : UIStackView!
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init(coder: NSCoder) {
super.init(coder: coder)
xibSetup() }
func xibSetup() {
contentView = loadViewFromNib()
contentView.frame = bounds
contentView.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(contentView)
}
func loadViewFromNib() -> UIStackView! {
let view: UIStackView? = Bundle.main.loadNibNamed("stackView", owner: nil, options: nil)?.first as! UIStackView?
return view
}
Create a viewController.Add a stackView to it.In StackView properties, goto 3rd bar which named as custom class, for class name give stackView class name
So I wrote my own custom view with its own initializer. However, when my main view loads my custom view gets depicted in a wrong way. It takes bounds as 600x600 rectangle, while the superview is 375x607. I did try to put auto constraint, seems not to work. I tried to do it programmatically in the subview initialization, but whenever I try to initialize it's bounds property to its superview bounds I get nil in superview.
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
func setup() {
initPathLayer()
initHandleView()
initHandlePanGestureRecognizer()
layoutPathLayer()
layoutHandleViews()
}
I tried everything there is on the internet to make subview fill its superview, but I think that subview gets initialized before superview? Is that possible ? In the ViewController a have my custom view declared as an Outlet connection. I'm sure that the problem should be super easy and it's me who doesn't know the way Swift initializes the view.
Any ideas ?
Thank You.
Firstly, a Views init method is not the best place to perform a layout. The view could be resized at a later point which is typically always the case if the view is loaded from a Xib or you are creating it in a View Controllers viewDidLoad function.
That being said you have several approaches:
1. Use Auto Layout
Interface Builder
This can be done either in Interface Builder or programmatically. In Interface Builder you simply use the 'Pin' option and select all the sides of the view
When this is done you should be able to see your constraints in the Size inspector looking as follows:
Programmatically
Alternatively you can always add your constraints programmatically in your initializer:
override init(frame: CGRect) {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
super.init(frame: frame)
let viewsDict = ["view": view]
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view]-0-|", options: .allZeros, metrics: nil, views: viewsDict))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view]-0-|", options: .allZeros, metrics: nil, views: viewsDict))
addSubview(view)
}
convenience required init(coder aDecoder: NSCoder) {
self.init(frame: .zero)
}
2. Manual Layout
Resizing Masks
For this you can either use resizing masks or you can control the frame in layoutSubviews. Resizing masks tell the system how the view should be sized relative to the superview:
override init(frame: CGRect) {
let view = UIView(frame: CGRectZero)
view.translatesAutoresizingMaskIntoConstraints = false
super.init(frame: frame)
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
}
Layout Subviews
Lastly, you can override layoutSubviews and go from there:
override func layoutSubviews() {
super.layoutSubviews()
view.frame = bounds
}