SOLUTION FOUND - credit Sanzio Angeli, used lazy var
Trying the access the pageIndex in order to update the page indicator , i try and create an instance of a class which has the public property of pageIndex, but the moment i try and do so its crashing the app, can any on one please suggest where i am doing wrong, Strange enough if i do not declare the instance globally but inside a method, the app does not crash
Error i get - Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeeebccea8)
Below is class whose instance i am trying to create and after that the class where i am trying to call it by creating a global instance at top, i am trying to use it in func moveToNext()
import UIKit
class ContentViewController: UIViewController {
let contentDesign = ContentView()
var pageIndex = 0
var pageHeading = ""
var pageContent = ""
var pageImage = ""
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(contentDesign)
contentDesign.pagerContent.text = pageContent
contentDesign.pagerHeader.text = pageHeading
contentDesign.pagerImage.image = UIImage(named: pageImage)
// Do any additional setup after loading the view.
}
}
//////---------
class MasterView: UIViewController {
var container = UIView()
var lowerCotainer = UIView()
var pageNumbering: UIPageControl = UIPageControl()
var nextButton = UIButton()
var skipButton = UIButton()
var pageController = PageViewController()
**var content = ContentViewController()**
override func viewDidLoad() {
super.viewDidLoad()
commonInit()
}
func commonInit()
{
container.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(container)
container.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
container.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height * 0.6).isActive = true
}
#objc func moveToNext() {
// let index = pageController.currentIndex
let index = content.pageIndex
pageNumbering.currentPage = index + 1
print(index)
switch index {
case 0...1:
pageController.forwardPage()
case 2:
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
default:
break
}
}
}
Where the app crashes is strange here in another class at var pagerHeader = UILabel()
import UIKit
class ContentView: UIView {
var pagerImage = UIImageView()
var pagerHeader = UILabel()
var pagerContent = UILabel()
let master = MasterView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func commonInit()
{
pagerImage.translatesAutoresizingMaskIntoConstraints = false
addSubview(pagerImage)
}
}
After working with multiverse:
lazy var content = ContentViewController()
Instead of:
var content = ContentViewController()
Has fixed the issue
Related
I am creating a custom UIView in swift, here is the code for the custom View,
class FormField: UIView {
#IBOutlet var viewLabel : UILabel!
#IBOutlet var textField: UITextField!
#IBOutlet var separator: UIView!
private var verticallyCenteredAnchor : NSLayoutConstraint!
private var topPositionedAnchor : NSLayoutConstraint!
private var isViewHighlighted = false
private var view: UIView?
let nibName = "FormField"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override class var requiresConstraintBasedLayout: Bool {
return true
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
self.view = view
view.frame = self.bounds
self.addSubview(view)
verticallyCenteredAnchor = viewLabel.centerYAnchor.constraint(equalTo: self.view!.centerYAnchor)
verticallyCenteredAnchor.isActive = false
topPositionedAnchor = viewLabel.topAnchor.constraint(equalTo: self.view!.topAnchor,constant: 0)
topPositionedAnchor.isActive = true
let userNameTap = UITapGestureRecognizer(target: self, action: #selector(animateLabel))
userNameTap.numberOfTapsRequired = 1
view.addGestureRecognizer(userNameTap)
}
#objc func animateLabel() {
if(self.isViewHighlighted) {
verticallyCenteredAnchor.isActive = true
topPositionedAnchor.isActive = false
} else {
verticallyCenteredAnchor.isActive = false
topPositionedAnchor.isActive = true
}
self.isViewHighlighted = !self.isViewHighlighted
UIView.animate(withDuration: 0.5) {
self.view!.layoutIfNeeded()
}
}
func loadViewFromNib() -> UIView? {
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
I am using a xib file for the view and constraints are set in that. Now when someone taps on the view I am trying to change the constraints with animation. But that's not working, no layout change is happening when the user taps on the view. I can confirm that the animateLabel() method is called when the user taps.
Now if is add in the line,
self.view!.translatesAutoresizingMaskIntoConstraints = false
all constraints are messed up, it's not honouring the constraints I already set in the xib.
What's going wrong here?
I am using a xib file for the view and constraints are set in that.
You have conflicts in your constraints who are in xib + in code , you need to hook the constraints in xib as outlets and play with their .isActive property and completely remove the code constraints
I've a custom text field in which I'm adding an error label below text field. I want to resize this custom text field so that it expands with multiple line error label and doesn't overlap with other fields below it. In IB I've pinned view properly so that is not an issue.
How to fix it?
class LoginViewController: UIViewController {
#IBOutlet weak var emailTextField: CustomTextField!
#IBOutlet weak var passwordTextField: CustomTextField!
override func viewDidLoad() {
super.viewDidLoad()
emailTextField.setError("Multiple line error. Multiple line error. Multiple line error. Multiple line error.")
}
}
class CustomTextField: UITextField {
var bottomBorder = UIView()
var errorLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.initialize()
// Setup Bottom-Border
// ....
errorLabel.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(errorLabel)
errorLabel.topAnchor.constraint(equalTo: self.bottomBorder.bottomAnchor, constant: 4).isActive = true
errorLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
errorLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
errorLabel.numberOfLines = 0
errorLabel.lineBreakMode = .byWordWrapping
errorLabel.sizeToFit()
}
func initialize() {
self.text = ""
self.clearError()
// ...
}
func setError(error: String) {
self.errorLabel.text = error
self.errorLabel.isHidden = false
self.setNeedsLayout()
self.layoutIfNeeded()
}
func clearError() {
self.errorLabel.text = ""
self.errorLabel.isHidden = true
}
}
UITextField is only 1 line you need to use UITextView or better do
class CustomView: UIView {
let textfield = UITextField()
let bottomBorder = UIView()
let errorLabel = UILabel()
.....
}
So the view will expand according to sum of textfield height , border height and label text height
I have a UIView which have a button and some view to indicate sucess and failure. I am trying to use that UIView on other view controllers and receive the button action on called view controllers.
This is what i have tried so far
protocol FailViewDelegate: class {
func tryAgainTapped()
}
class AlertView: UIView {
static let instance = AlertView()
weak var delegate : FailViewDelegate?
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var messageLbl: UILabel!
#IBOutlet weak var dashIMageView: AnimatableImageView!
#IBOutlet weak var circleView: AnimatableView!
#IBOutlet weak var iconStatus: AnimatableImageView!
#IBOutlet weak var tryAgainButton: AnimatableButton!
#IBOutlet weak var parentView: UIView!
private override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed("AlertView", owner: self, options: nil)
}
enum AlertType {
case success
case failure
}
func showAlert(alertType: AlertType, to: UIViewController) {
switch alertType {
case .success:
dashIMageView.image = UIImage(named: "circle-dash-blue")
circleView.backgroundColor = UIColor(hexString: "#4EBFFF")
titleLbl.text = "Success"
titleLbl.textColor = UIColor(hexString: "#4EBFFF")
messageLbl.text = "Your ticket has been created."
tryAgainButton.isHidden = true
iconStatus.image = UIImage(named: "icon-check")
case .failure:
dashIMageView.image = UIImage(named: "circle-dash-red")
circleView.backgroundColor = UIColor(hexString: "#EB3708")
titleLbl.text = "Failure"
titleLbl.textColor = UIColor(hexString: "#EB3708")
messageLbl.text = "There was an error, creating your ticket."
tryAgainButton.isHidden = false
iconStatus.image = UIImage(named: "icon-close")
}
parentView.center = to.view.center
to.view.addSubview(parentView)
}
func dismissAlert() {
parentView.removeFromSuperview()
}
#IBAction func tryAgainButtonTapped(_ sender: AnimatableButton) {
delegate?.tryAgainTapped()
}
}
This is how i have called the view
class CreateTicketViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
AlertView.sharedInstance.delegate = self
}
#IBAction func createTicketTapped(_ sender: AnimatableButton) {
AlertView.sharedInstance.showAlert(alertType: .failure, to: self)
}
}
extension CreateTicketViewController : FailViewDelegate {
func tryAgainTapped() {
print("Try Again Tapped")
}
}
This is the error that i got
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
(in dashIMageView.image = UIImage(named: "circle-dash-red")) and when i remove the dashImageView then the error occur for nextView and so on
You don't need to make it a singleton, which in this case (View) is a very uncommon approach, I think. You can create as many instances of that view as you want any time, anywhere (on other ViewControllers) you want and specify them, the way you want them to have.
When you want a view to be rendered and be visible, it always must be part of the view hierarchy in the current visible view controllers main view. And a view can only have ONE SUPERVIEW at the time, so whenever you add a (singleton) view to another superview, it will be removed from an other superview. If you want the same view on many view controllers (no problem), just don't let it be a singleton.
So first thing to do -> Remove the singleton design by commenting out that line:
class AlertView: UIView {
// make this line a comment or just remove it
// static let instance = AlertView()
weak var delegate : FailViewDelegate?
In your different view controllers you just create that instances of your AlertView and set the delegate correctly like this:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
myAlertView = AlertView()
myAlertView.delegate = self
// then you don't need this anymore
// AlertView.sharedInstance.delegate = self
}
Now I'm practicing build IOS app without using storyboard , but I have a problem want to solve , I created a custom UIView called BannerView and added a background(UIView) and a title(UILabel) , and called this BannerView in the MainVC , but run this app , it crashes at the function setupSubviews() and I don't know why.
import UIKit
import SnapKit
class BannerView: UIView {
var background: UIView!
var title: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupSubviews()
}
func setupSubviews() {
background.backgroundColor = .gray
title.text = "INHEART"
self.addSubview(background)
self.addSubview(title)
}
override func layoutSubviews() {
background.snp.makeConstraints { make in
make.width.equalTo(ScreenWidth)
make.height.equalTo(BannerHeight)
make.left.top.right.equalTo(0)
}
title.snp.makeConstraints { make in
make.width.equalTo(100)
make.center.equalTo(background.snp.center)
}
}
}
class MainVC: UIViewController {
var bannerView:BannerView!
override func viewDidLoad() {
super.viewDidLoad()
bannerView = BannerView(frame: CGRect.zero)
view.addSubview(bannerView)
}
}
Your properties do not appear to be initialised
var background: UIView!
var title: UILabel!
You should initialize these in your init method
self.background = UIView()
self.title = UILabel()
If you use force unwrapping on a class property you must initialize in the init method. XCode should be complaining to you about this and your error message should show a similar error
You are not initialised the background view please initialised them
self.background = UIView()
self.title = UILabel()
and if you want to create custom view by use of xib the follow them Custum View
You must have to initialised the self.background = UIView() and self.title = UILabel() first.
You can initalised them in setupSubviews() function before the set/assign values to them.
I'm very new to swift so sorry if this is a basic question or I'm doing something terribly wrong. I've been having some issues trying to add a subview when touching a row on a tableview and have been working off of this page: http://myxcode.net/2015/11/07/adding-a-subview-using-a-xib-and-storyboard/
Here's the relevant code I have so far (I removed some of the tableview logic because that works fine):
Subview Class
class ConfirmTeamView: UIView, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var teamListTableView2: UITableView!
#IBOutlet weak var confirmButton2: UIButton!
#IBOutlet weak var cancelButton2: UIButton!
var playerList: [Player]?
var view: UIView!
init(pList: [Player]) {
self.playerList = pList
super.init(frame: CGRectMake(20, 100, 385, 339))
setup()
teamListTableView2.delegate = self
teamListTableView2.dataSource = self
let playerListTableCellNib = UINib(nibName: "PlayerListTableViewCell", bundle: nil)
teamListTableView2.registerNib(playerListTableCellNib, forCellReuseIdentifier: "PlayerListTableViewCell")
}
required init?(coder aDecoder: NSCoder) {
self.playerList = nil
super.init(coder: aDecoder)
setup()
}
func setup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.FlexibleHeight,UIViewAutoresizing.FlexibleWidth]
addSubview(view)
}
func loadViewFromNib () -> UIView
{
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "ConfirmTeamView", bundle: bundle)
//this line I think is causing a stack overflow, any idea why?
let thisview = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return thisview
}
}
and then in my viewController (I again removed some tableview methods for ease of reading):
class ChooseExistingTeamViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var TeamListTableView: UITableView!
var existingTeamsArray = [Team]()
var confirmTeamUI : ConfirmTeamView!
override func viewDidLoad() {
super.viewDidLoad()
TeamListTableView.delegate = self
TeamListTableView.dataSource = self
let testPlayer1 = Player(name: "John", number: 24)
let testPlayer2 = Player(name: "Smith", number: 50)
let testTeam = Team(name: "myTeam", players: [testPlayer1, testPlayer2])
existingTeamsList = [testTeam]
}
override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() }
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// This is where I load the subview
TeamListTableView.userInteractionEnabled = false
let selectedTeam = existingTeamsList[indexPath.row]
let confirmTeamUI = ConfirmTeamView(pList: selectedTeam.players)
confirmTeamUI.cancelButton2.addTarget(self, action: "cancelPressed:", forControlEvents: UIControlEvents.TouchUpInside)
let viewWidth = self.view.frame.width
let xWidth = viewWidth - 40
let yHeight = 200
confirmTeamUI.frame = CGRect(x: 20, y: 100, width: Int(xWidth), height: yHeight)
self.view.addSubview(confirmTeamUI)
}
func cancelPressed(sender: UIButton) {
// self.confirmTeamUI.view.removeFromSuperview() [This line throws an exception for unwrapping an optional value]
if self.confirmTeamUI != nil { self.confirmTeamUI.removeFromSuperview() }
else { print("Confirm Team is nil") }
// pressing the cancel button runs the else case
}
Any advice would be very appreciated and please let me know if you need more!