Swift custom xib file is not centered correctly - ios

I am having problems with my Xib UIView.I created an xib view, added it to a uiview class, and then called this class in the main viewcontroller. In my xib view I created a label which is centered there directly in the center. Only when I start my project is the view no longer displayed in the center:
Look
But this is what my view looks like in the xib file:
Look
Could it be that it is because of the navigation controller? Although I don't think so, because I also added the navigation controller to my xib file. By the way, my custom view is added to a scrollview in my main viewcontroller.
This is my custom view class:
import UIKit
class TimerView: UIView {
#IBOutlet var contentView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func commonInit() {
Bundle.main.loadNibNamed("TimerView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
}
and there I added it to the scrollview:
func setUpScrollView() {
sequenzeScrollView.contentSize = CGSize(width: view.frame.size.width * 3, height: 0)
sequenzeScrollView.isPagingEnabled = true
sequenzeScrollView.showsVerticalScrollIndicator = false
sequenzeScrollView.showsHorizontalScrollIndicator = false
sequenzeScrollView.delegate = self
for i in 0..<3 {
let timerView = TimerView(frame: CGRect(x: view.frame.size.width * CGFloat(i), y: 0, width: view.frame.size.width, height: view.frame.size.height))
sequenzeScrollView.addSubview(timerView)
}
}
can someone help me with this? I would be really happy about that! Love greetings

Related

Creating custom UIView, or child VCs

So, i have the following case. I got a custom tab layout, ie a horizontal UIScollview with paging. In each page there are some controls and a UITablevie, "bundles" in a custom UIView.
I create a ContainerVC, which has the scrollview, and then I initialize the two UIViews and add them as subviews in ContainerVC. To do so, i have created a custom UIView class and have have set this UIView as the owner of the .xib file. I also add the .xib class as my custom UIView.
Although this works, at least UI wise, i have some functionality problems, like the following.
In the init method of each UIView I initialize the view with
Bundle.main.loadNibNamed(kCONTENT_XIB_NAME, owner: self, options: nil)
and then set the UITableViewDelegate and UITableViewDateSource to self.
However, when the data that feed the table arrives in the view, and i reload the tableview, nothing happens (the reload method is run in the main Thread), until i scroll the UITableview.
Is this the correct course of action?
VC
func createUsageHistoryTabs() -> [Consumption] {
var tabs = [Consumption]()
let v1 = Consumption(pageIndex: 0, viewModel: viewModel)
let v2 = Consumption(pageIndex: 1, viewModel: viewModel)
tabs.append(v1)
tabs.append(v2)
return tabs
}
func setupScrollView() {
if(!hasBeenCreated) {
let tabs = createUsageHistoryTabs()
scrollview.frame = CGRect(x: 0, y: 0, width: container.frame.width, height: container.frame.height)
scrollview.contentSize = CGSize(width: view.frame.width * CGFloat(tabs.count), height: 1.0)
scrollview.isPagingEnabled = true
for i in 0 ..< tabs.count {
let slideView = tabs[i]
slideView.frame = CGRect(x: view.frame.width * CGFloat(i), y: scrollview.frame.origin.y, width: scrollview.frame.width, height: scrollview.frame.height)
scrollview.addSubview(slideView)
}
hasBeenCreated.toggle()
}
}
UIView:
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
convenience init(pageIndex: Int, viewModel: UsageHistoryViewModel) {
self.init(frame: CGRect.zero)
self.pageIndex = pageIndex
self.viewModel = viewModel
commonInit()
}
func commonInit() {
Bundle.main.loadNibNamed(kCONTENT_XIB_NAME, owner: self, options: nil)
contentView.fixInView(self)
setupView()
footer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleFooterTap)))
viewModel?.cdrDelegate = self
tableView.register(EmptyCell.self, forCellReuseIdentifier: "EmptyCell")
tableView.register(UINib(nibName: "TVCell", bundle: nil), forCellReuseIdentifier: "TVCell")
tableView.allowsSelection = false
tableView.delegate = self
tableView.dataSource = self
}
In this func:
func setupScrollView() {
if(!hasBeenCreated) {
// this line creates a LOCAL array
let tabs = createUsageHistoryTabs()
scrollview.frame = CGRect(x: 0, y: 0, width: container.frame.width, height: container.frame.height)
scrollview.contentSize = CGSize(width: view.frame.width * CGFloat(tabs.count), height: 1.0)
scrollview.isPagingEnabled = true
for i in 0 ..< tabs.count {
let slideView = tabs[i]
slideView.frame = CGRect(x: view.frame.width * CGFloat(i), y: scrollview.frame.origin.y, width: scrollview.frame.width, height: scrollview.frame.height)
scrollview.addSubview(slideView)
}
hasBeenCreated.toggle()
}
}
You are creating a local array of Consumption objects. From each object, you add its view to your scrollView. As soon as that func exits, tabs no longer exists... it goes "out-of-scope".
You want to create a class-level var:
var tabsArray: [Consumption]?
and then modify that var instead of creating a local instance:
func setupScrollView() {
if(!hasBeenCreated) {
// this line creates a LOCAL array
tabsArray = createUsageHistoryTabs()
...
Your custom classes should then be available until you delete them (or tabsArray goes out-of-scope).
You may need to make some other changes, but that should, at least, be a start.
EDIT after comment discussion...
If the tableviews are working, but not "updating" until the are scrolled - you said reload is being called on the main thread - you might try either of these options (or both... experiment):
tableView.reloadData()
tableView.setNeedsLayout()
tableView.layoutIfNeeded()
tableView.reloadData()
tableView.beginUpdates()
tableView.endUpdates()

custom header UIView to all the VC?

I am trying to create a custom header file to all the ViewControllers in project. The following image is an actual design.
So that I am trying to create Custom view with Xib file as below :
import UIKit
class HamburgrView: UIView {
override init(frame: CGRect) {
// 1. setup any properties here
// 2. call super.init(frame:)
super.init(frame: frame)
// 3. Setup view from .xib file
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
override open func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
xibSetup()
}
override func layoutSubviews() {
}
func xibSetup() {
let tempView = loadViewFromNib()
// use bounds not frame or it'll be offset
tempView.frame = bounds
// Make the view stretch with containing view
tempView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(tempView)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of:self))
let nib = UINib(nibName: "HamburgrView", bundle: bundle)
// Assumes UIView is top level and only object in CustomView.xib file
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
}
We don't know how to draw that Hamburger menu icon with curve and its start position from SafeArealayoutGuide.
I trying one view with x position negative -40 and giving cornerRadius not coming perfect curve like above image.
Output look not good.
My ViewController Code :
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let hamView = HamburgrView(frame: .zero)
self.view.addSubview(hamView)
hamView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hamView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0),
hamView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0),
hamView.widthAnchor.constraint(equalToConstant: 120),
hamView.heightAnchor.constraint(equalToConstant: 80)
])
}
As #sandip answer I have changed like below :
func xibSetup() {
let tempView = loadViewFromNib()
// use bounds not frame or it'll be offset
tempView.frame = bounds
// Make the view stretch with containing view
tempView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(tempView)
let layer = CAShapeLayer()
layer.path = UIBezierPath.init(arcCenter: CGPoint(x: 0, y: self.center.y), radius: 50, startAngle: -1.5708, endAngle: 1.5708, clockwise: true).cgPath
layer.fillColor = UIColor.red.cgColor
self.layer.addSublayer(layer)
}
Output Looks Below :
Any idea would be appreciate.
Is this what you wanted?
You can easily achieve it with CAShapeLayer and UIBezierPath
let layer = CAShapeLayer()
layer.path = UIBezierPath.init(arcCenter: CGPoint(x: 0, y: self.view.center.y), radius: 30, startAngle: -1.5708, endAngle: 1.5708, clockwise: true).cgPath
layer.fillColor = UIColor.red.cgColor
view.layer.addSublayer(layer)
In your code change arc centre as x to be zero and y to hamburger view's vertical centre and add it to self.view
Hope it helps
EDIT 1:
As OP has asked for complete code in comment updating the answer
class HamburgrView: UIView {
override init(frame: CGRect) {
// 1. setup any properties here
// 2. call super.init(frame:)
super.init(frame: frame)
// 3. Setup view from .xib file
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
override open func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
xibSetup()
}
override func layoutSubviews() {
}
func xibSetup() {
let tempView = loadViewFromNib()
// use bounds not frame or it'll be offset
tempView.frame = bounds
// Make the view stretch with containing view
tempView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(tempView)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of:self))
let nib = UINib(nibName: "HamburgrView", bundle: bundle)
// Assumes UIView is top level and only object in CustomView.xib file
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
let layer = CAShapeLayer()
layer.path = UIBezierPath.init(arcCenter: CGPoint(x: 0, y: 40), radius: 50, startAngle: -1.5708, endAngle: 1.5708, clockwise: true).cgPath
layer.fillColor = UIColor.red.cgColor
self.layer.addSublayer(layer)
}
}
EDIT 2:
Reason OPs arc coming outside hamburger view is because of wrong arc centre
changing to CGPoint(x: 0, y: 40) from self.view.center.y should solve the issue. Updated the code to reflect the same
Why 40?
Because OP is setting height of hamburger view to 80. So 80/2 = 40
Why cant we use bounds.size.height / 2 or self.view.center.y?
because in didMoveToSuperview OP's constraint has not kicked in yet so it is taking the height view in xib which I don't think is 80 so it sets the arc centre with that value but then your constraint changes height to 80.
Here is final O/P:

When inheriting UIView and creating an instance for the new class, it does not create a view

I have created a new class inheriting UIView. When I create an instance of the new class, the view does not seem to be created.
This is my class.
class QuestionView: UIView {
var metrics : [String : CGFloat] = [:]
override init(frame : CGRect){
super.init(frame: frame)
self.backgroundColor = UIColor.blue
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here I have created an instance of this class to have the following values
x = 0, y = middle of the screen, width = as wide as the screen,
height = 400
class mainView : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var questionView = QuestionView(frame: CGRect(x: 0, y: self.view.frame.height/2, width: self.view.frame.width, height: 400))
self.view.addSubView(questionView)
}
}
I find no view being created.
Add subview(questionView) in mainView class in viewDidLoad method.
self.view.addSubview(questionView)
you can also test the view by setting the background color
questionView.backgroundColor = UIColor.red
You should add all views created programmatically in viewDidLayoutSubView like below
override func viewDidLayoutSubviews() {
var questionView = QuestionView(frame: CGRect(x: 0, y: self.view.frame.height/2, width: self.view.frame.width, height: 400))
self.view.addSubView(questionView)
}
Try following code to get the view in front, use bringSubviewToFront function.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let questionView = QuestionView(frame: CGRect(x: 0, y: self.view.frame.height/2, width: self.view.frame.width, height: 400))
self.view.addSubview(questionView)
self.view.bringSubviewToFront(questionView)
}

Not able to show border color and background as blur in custom view as popup view in Swift

I have some requirement to show popup as some customisation options.
So, I took custom view as UIView with Xib file.
class CustomAlertView : UIView {
#IBOutlet weak var customAlertView : UIView!
#IBOutlet weak var button1 : UIButton!
#IBOutlet weak var button2 : UIButton!
override init(frame: CGRect) { // for using CustomView in code
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) { // for using CustomView in IB
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed("CustomAlertView", owner: self)
guard let content = customAlertView else { return }
content.frame = self.bounds
content.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.addSubview(content)
}
}
And given outlet connections.
And I am able to load the view in viewcontroller class.
But, the issue is, its not showing as pop up view and its not showing any border color, etc even I added too.
And the present self.view (Main view from viewcontroller) still moving it has tableview, while I clicking on the buttons on custom view, nothing happening.
func someAction() {
self.view.addSubview(customAlert)
self.customAlert.layer.cornerRadius = 13
self.customAlert.layer.borderWidth = 10
self.customAlert.layer.borderColor = UIColor.gray.cgColor
self.customAlert.clipsToBounds = false
let radius: CGFloat = self.customAlert.frame.width / 2.0 //change it to .height if you need spread for height
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: self.customAlert.frame.height))
self.customAlert.layer.masksToBounds = false
self.customAlert.layer.cornerRadius = 8; // if you like rounded corners
self.customAlert.layer.shadowOffset = CGSize(width:-15, height:20);
self.customAlert.layer.shadowRadius = 5;
self.customAlert.layer.shadowOpacity = 0.5;
self.customAlert.layer.shadowPath = shadowPath.cgPath
self.customAlert.byMonthlyBtn.addTarget(self, action: #selector(button1tapped), for: .touchUpInside)
self.customAlert.byAnnuallyBtn.addTarget(self, action: #selector(button2tapped), for: .touchUpInside)
}
And its looks like below screenshot
Any suggestions?
private func commonInit() {
Bundle.main.loadNibNamed("CustomAlertView", owner: self)
guard let content = customAlertView else { return }
content.frame = self.bounds
content.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.addSubview(content)
}
Your problem is you are using the CustomAlertView you loaded. Try adjust your code like below.
private func commonInit() {
guard let view = Bundle.main.loadNibNamed("CustomAlertView", owner: self)?.first as? CustomAlertView else {
return
}
view.frame = bounds
view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addSubview(view)
}

UICollectionReusableView Header not resizing on rotation - constraints?

I have a UICollectionReusableView for a UICollectionView Header Section in code as follows:
var headingLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
headingLabel = UILabel(frame: CGRectMake(12.0,12.0,frame.width,21.0))
headingLabel.numberOfLines = 1
headingLabel.textAlignment = NSTextAlignment.Center
headingLabel.textColor = UIColor.blackColor()
self.backgroundColor = UIColor.whiteColor()
self.addSubview(headingLabel)
}
I just want the heading to be centered in the display. This is fine so long as I don't rotate the device. If I rotate the device, the label is no longer in the center of the screen.
My guess is this is super simple, I just can't seem to find the answer. My assumption is that I could just programatically add constraints, but I can't quite work out how to do that.
Alternatively, I think there might be some way to force it to redo the headers on a layout change, but I couldn't get that working either.
Any pointers in the right direction would be hugely appreciated.
Many thanks.
You have to invalidate the layout BEFORE the collectionView is re-laid out (which happens in viewDidLayoutSubviews), therefore you must invalidate it in viewWillLayoutSubviews.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// Handle rotation
collectionView.collectionViewLayout.invalidateLayout()
}
import Foundation
import UIKit
import MaterialComponents
class SectionHeaderAppointment:UICollectionReusableView {
var sectionHeaderlabel: UILabel!
var nameflatButton:MDCButton!
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
print("Rotation")
sectionHeaderlabel.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
nameflatButton.frame = CGRect(x: (frame.width - (frame.width/3))/2, y: 6, width: frame.width/3, height: frame.height - 8)
}
}

Resources