Custom UIView label and animation not showing up in view controller - ios

I actually have a custom uiview that works, and I think everything in the uiview that doesn't work is set up the same as the one that works. Here is the code:
protocol SessionDisplayViewDelegate: class
{
func homeButtonTapped()
}
class SessionDisplayView: UIView
{
#IBOutlet var view: UIView!
#IBOutlet weak var accountImage: UIButton!
#IBOutlet weak var mySKView: SKView!
#IBOutlet weak var sessionTitle: UILabel!
weak var delegate: SessionDisplayViewDelegate?
required init(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)!
commonInitialization()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInitialization()
}
func commonInitialization()
{
Bundle.main.loadNibNamed("SessionDisplayView", owner: self, options: nil)
self.addSubview(self.view)
//accountImage = UIImage(data: ShareData.sharedInstance.accounts[ShareData.sharedInstance.indexOfCurrentAccount].picture!, scale: 1.0)
}
func onView()
{
let curScene = MyScene(size: mySKView.bounds.size)
curScene.scaleMode = SKSceneScaleMode.aspectFill
mySKView.presentScene(curScene)
let myImage = UIImage(data: (ShareData.sharedInstance.accounts[ShareData.sharedInstance.indexOfCurrentAccount]?.picture!)! as Data, scale: 0.5)
accountImage.setImage(myImage, for: UIControlState())
sessionTitle.text = ShareData.sharedInstance.accounts[ShareData.sharedInstance.indexOfCurrentAccount]?.name
var myTest = sessionTitle.text
self.view.setNeedsDisplay()
}
#IBAction func homeButtonTouched(_ sender: UIButton)
{
delegate?.homeButtonTapped()
}
}
I don't know that the self.view.setNeedsDisplay() needs to be called- I'm just trying to make it work. The title doesn't change even though the variable for the title does change- I've checked that, and that is working. Either there's a connection issue between the label and the variable, or the view controller isn't getting the update signal to change the view controller. I don't know which- and I don't know how to nail down which it is, either. Any ideas on how to fix this would be deeply appreciated. Here is the ViewController code:
class SessionDisplayViewController: UIViewController, SessionDisplayViewDelegate
{
#IBOutlet weak var mySessionView: SessionDisplayView!
func homeButtonTapped()
{
self.performSegue(withIdentifier: "ReturnHome", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
mySessionView.onView()
mySessionView.sessionTitle.text = "Test"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var shouldAutorotate : Bool {
if (UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft) || (UIDevice.current.orientation == UIDeviceOrientation.landscapeRight) || (UIDevice.current.orientation == UIDeviceOrientation.unknown)
{
return true
}
else
{
return false
}
}
override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
return [UIInterfaceOrientationMask.portrait, UIInterfaceOrientationMask.portraitUpsideDown]
}
override var preferredStatusBarStyle : UIStatusBarStyle
{
return UIStatusBarStyle.lightContent
}
}
Any thoughts or suggestion on how to get the UIView to update would be most welcome. Thanks.
Sincerely,
Sean

How does you add your custom view to your root view? From code or from xib/storyboard? If you done that by xib/storyboard you must override awakeFromNib method in your custom view class and call commonInitialization inside it. init() method was call only if you create your view by code.

Related

How do I fire an event from a button inside a child view to trigger an event on parent View? Button is not working

I am trying to create a verification process in 4 steps, in order to make my code more efficient, I decided to use Child views and just update the UI accordingly.
I managed to add my child view to my MasterView, however, I am unable to click the button inside my child view.
I already checked the view hierarchy and there is nothing on top of my button. I also tried to add the action programmatically, re-added the button but I can't make it work. I am new to swift development so probably I am missing something.
Child view Code
protocol IdentityVerificationIntroChildViewControllerDelegate{
func startVIProcess(_ sender: Any)
}
class IdentityVerificationIntroChildViewController: UIView{
#IBOutlet var contentView: UIView!
var delegate: IdentityVerificationIntroChildViewControllerDelegate?
#IBOutlet weak var mStartVIBtn: UIButton!
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("IdentityVerificationIntroChildView", owner: self, options: nil)
mStartVIBtn.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
#objc func buttonAction(sender: UIButton!) {
print("works")
}
}
Master view code
final class MasterIdentityVerificationViewController: UIViewController {
#IBOutlet weak var mContainerView: UIView!
#IBOutlet weak var mStepIndicator: StepIndicatorView!
private var currentView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
setChildView(subView: IdentityVerificationIntroChildViewController())
}
private func setChildView(subView: UIView){
currentView?.removeFromSuperview()
currentView = subView
currentView?.translatesAutoresizingMaskIntoConstraints = false
guard let currentView = currentView else { return }
mContainerView.addSubview(currentView)
NSLayoutConstraint.activate([
currentView.topAnchor.constraint(equalTo: mContainerView.topAnchor),
currentView.trailingAnchor.constraint(equalTo: mContainerView.trailingAnchor),
currentView.leadingAnchor.constraint(equalTo: mContainerView.leadingAnchor),
currentView.bottomAnchor.constraint(equalTo: mContainerView.bottomAnchor)
])
}
}
extension MasterIdentityVerificationViewController: IdentityVerificationIntroChildViewControllerDelegate{
func startVIProcess(_ sender: Any) {
performSegue(withIdentifier: "fromVIIntroToVIIDCapture", sender: sender)
print("adfsdfs")
}
}
View Hierarchy, the problematic button is Highlighted
Green Area is where Child views are getting switched
I would really appreciate any help. Thanks

Custom Alert creating Fatal error: init(coder:) has not been implemented

Good Day,
I want to create a custom alert for gather information from the user. However, it will not load and crashes with the following code:
xib file
import Foundation
import UIKit
class WorkoutAlert: UIView {
static let instance = WorkoutAlert()
#IBOutlet var parentView: UIView!
#IBOutlet weak var alertView: UIView!
#IBOutlet weak var titleLabel: UILabel!
#IBAction func workoutTextField(_ sender: UITextField) {
}
#IBAction func datePicker(_ sender: UIDatePicker) {
}
#IBAction func cancelButtonPressed(_ sender: UIButton) {
parentView.removeFromSuperview()
}
#IBAction func doneButtonPressed(_ sender: UIButton) {
}
override init(frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("WorkoutAlert", owner: self, options: nil)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func commonInit () {
alertView.layer.cornerRadius = 10
parentView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
parentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
func showAlert(title: String) {
self.titleLabel.text = title
UIApplication.shared.keyWindow?.addSubview(parentView)
}
// MARK: - End of Code
}
Relevant code for the ViewController using the xib:
class WorkoutViewController: UIViewController {
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
WorkoutAlert.instance.showAlert(title: "Add a new workout")
}
}
Constraints for the xib
Any assistance is greatly appreciated. I'm a novice and have found many ways and examples for creating alerts instead of using the standard alert controller.
You need to implement init methods according to your needs. But better to implement boths and you don't need singleton pattern here
class WorkoutAlert: UIView {
#IBOutlet var parentView: UIView!
#IBOutlet weak var alertView: UIView!
#IBOutlet weak var titleLabel: UILabel!
#IBAction func workoutTextField(_ sender: UITextField) {
}
#IBAction func datePicker(_ sender: UIDatePicker) {
}
#IBAction func cancelButtonPressed(_ sender: UIButton) {
parentView.removeFromSuperview()
}
#IBAction func doneButtonPressed(_ sender: UIButton) {
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
public override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
private func commonInit () {
Bundle.main.loadNibNamed("WorkoutAlert", owner: self, options: nil)
alertView.layer.cornerRadius = 10
parentView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
parentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
func showAlert(title: String) {
self.titleLabel.text = title
UIApplication.shared.keyWindow?.addSubview(self)
}
// MARK: - End of Code
}
In your controller
class WorkoutViewController: UIViewController {
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
let alert = WorkoutAlert(frame: self.view.bounds) // or can set frame here WorkoutAlert(frame: frame here)
alert.showAlert(title: "Add a new workout")
}
}

Using UIView (Singleton) on different viewcontrollers

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
}

Is it a good way to pass data to custom view then execute the function?

I created a custom input accessory view, it is the submit button.
However, I need to pass the data to the custom view then execute the further function. It is a good way to do that?
class SignUpViewController: UIViewController {
#IBOutlet weak var phoneTF: SignLogTextField!
#IBOutlet weak var EmailTF: SignLogTextField!
#IBOutlet weak var PasswordTF: SignLogTextField!
#IBOutlet weak var FBBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
textFieldPreparation()
}
func textFieldPreparation(){
EmailTF.inputAccessoryView = Bundle.main.loadNibNamed("SignSubmitBTN", owner: self, options: nil)?.first as! SignSubmitBTN
phoneTF.inputAccessoryView = Bundle.main.loadNibNamed("SignSubmitBTN", owner: self, options: nil)?.first as! SignSubmitBTN
PasswordTF.inputAccessoryView = Bundle.main.loadNibNamed("SignSubmitBTN", owner: self, options: nil)?.first as! SignSubmitBTN
}
}
I am not sure how to pass the data to the custom view or should I do the sign up in the Outlet Action?
It is my custom view
import UIKit
class SignSubmitBTN: UIView {
#IBAction func submitAction(_ sender: Any) {
}
#IBOutlet weak var subBTN: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup(){}
}
If I have to pass data to custom view should I use protocol? If I should use the protocol of how to use it?
OK...
I think you are approaching this from the wrong direction. The responsibility of a button should be to tell you that a user has tapped it and nothing more. The button should not be dealing with signing in.
But... you are 90% of the way there here. Just a few more bits to add.
You can update your submit button to include a delegate and use the delegate in your button action...
import UIKit
// protocol
protocol SignInButtonDelegate: class {
func signIn()
}
class SignSubmitBTN: UIView {
// property for delegate
weak var delegate: SignInButtonDelegate?
#IBAction func submitAction(_ sender: Any) {
// this tells the delegate to sign in
// it doesn't need to know how that happens
delegate?.signIn()
}
#IBOutlet weak var subBTN: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {}
}
Then in your view controller you conform to the delegate protocol...
extension SignUpViewController: SignInButtonDelegate {
func signIn() {
// here you already have access to all the data you need to sign in.
// you are in the view controller here so just get the text from the username, password, etc...
}
}
And then set the view controller as the delegate...
func textFieldPreparation() {
let signInButton = Bundle.main.loadNibNamed("SignSubmitBTN", owner: self, options: nil)?.first as! SignSubmitBTN
signInButton.delegate = self
// these are properties... they should begin with a lowercase letter
emailTF.inputAccessoryView = signInButton
phoneTF.inputAccessoryView = signInButton
passwordTF.inputAccessoryView = signInButton
}
Your CustomView is just a class at the end, so you can do it in object oriented paratime, For that write a function in your customView to pass data in it. Like
class SignSubmitBTN: UIView {
var data: String!;
public func setData(data: String) {
self.data = data;
}
/// Other code
}
And to set data after initializing your CustomView, call setData(params) function to set data in it.
Try this
func loadFromNib() -> SignSubmitBTN {
let bundle = Bundle.main.loadNibNamed("SignSubmitBTN", owner: self, options: nil)?.first as! SignSubmitBTN
return bundle
}
In your viewcontroller call like below:
let customObj = loadFromNib()
customObj.dataToGet = "Data to pass"
customObj.delegate = self
EmailTF.inputAccessoryView = customObj
If you want pass data from custom class, You need to use delegate protocol as #Fogmeister suggested.
If you want delegate option
public protocol menuOpen: class {
func openMenuAction(selectedValue : String)
}
class SignSubmitBTN: UIView {
open var delegate:menuOpen?
var dataToGet = ""
#IBAction func submitAction(_ sender: Any) {
self.delegate.openMenuAction("test")
}
}
Then add delegate method in your VC
class SignUpViewController: UIViewController,menuOpen{
func openMenuAction(selectedValue : String) {
//get your selected value here, you would better pass parameter in this method
}
}

Custom UIScrollView class not initializing IBOutlets

I've recently dived into the world of Swift 3 and Xcode 8 and have been working on a simple game to learn as much as possible but I have run into an issue where my custom class is crashing when I segue into it because the IBOutlets which I dragged into it are nil.
I am using StoryBoard and AutoLayout and have dragged several fields into my class statistics:UIScrollView from the storyboard.
class StatisticsView: UIScrollView, sendLabelDelegate {
#IBOutlet weak var GamesWonNum: UILabel!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.GamesWonNum = aDecoder.decodeObject(forKey: "GamesWonNum") as! UILabel!
}
override func encode(with aCoder: NSCoder) {
aCoder.encode(self.GamesWonNum, forKey: "GamesWonNum")
}
func setLabel(data: String) {
let temp = Int(data)
setNumber(num: temp!)
}
override func awakeFromNib() { }
override init(frame: CGRect) {
super.init(frame: frame)
}
func setNumber(num: Int){
GamesWonNum.text = String(num)
}
}
I have made sure that the statistics class is set as the identifier for the storyboard scrollview. I've done a lot of research into the topic, and I couldn't find a concrete answer on exactly how this works. I found in another post that calling the function awakefromNib() will fix the issue and it has, but I want to understand what awakefromNib() does and why I cannot simply init() the view.
My view Controller hierarchy is as follows:
MainMenuVC | --Segue--> SettingsVC
MainMenyVC | --Segue--> StatisticsVC
class StatisticsVC: UIViewController, sendLabelDelegate{
#IBOutlet weak var StatsScrollView: UIScrollView!
var settingsLabel:String = ""
override func viewDidLoad() {
super.viewDidLoad()
if UIScreen.main.bounds.height < 600 {
StatsScrollView.contentSize.height = 700
print("screensizeif: \(UIScreen.main.bounds.height), \(StatsScrollView.contentSize.height)")
}else{
StatsScrollView.contentSize.height = UIScreen.main.bounds.height
print("screensizeelse: \(UIScreen.main.bounds.height), \(StatsScrollView.contentSize.height)")
}
StatsScrollView.contentSize.width = UIScreen.main.bounds.width
StatsScrollView.isUserInteractionEnabled = true
}
func setLabel(data: String) {
settingsLabel = data
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
}
My understanding is that the viewController will init all of its views before going into the viewdidLoad() function, so why is awakefromNib() necessary and what exactly should I define inside of it. Further, why can't I initialize the class inside init()? If I try to set the GamesWon Outlet inside init(), the outlet is nil.
Its a bit difficult for me to wrap my head around the relationship that exists here between a view controller, the custom scrollview I have, and the outlets being nil. If anyone could clarify I would very much appreciate it.
Thank you!

Resources