I subclassed UIImageView to add a custom property.
class ProfilePictureImageView: UIImageView {
var isAffirmed: Bool?
}
When trying to set isAffirmed, I get an EXC_BAD_ACCESS error. Here is a stripped down version of my class to show the error:
class SettingsTableViewController: UITableViewController {
#IBOutlet weak var userImageView: ProfilePictureImageView!
override func viewDidLoad() {
super.viewDidLoad()
userImageView.image = nil // This works
userImageView.isAffirmed = true //EXC_BAD_ACCESS error here
}
}
I'm able to access and set properties of UIImageView (such as image), but not properties from my subclass. What is the cause of this error?
Make sure that you have set the custom class for the UIImageView in your storyboard, otherwise you will get a plain old UIImageView and an exception when you access the new property
Sounds like you haven't changed your class from UIImageView to ProfilePictureImageView in Interface Builder.
Related
I have created a custom UIView and have a protocol set for it. Now from the View Controller when I set the delegate to self I am getting an EXC_BAD_ACCESS.
---The View Controller Code------
class VerificationController: UIViewController, LoadingViewDelegate {
#IBOutlet weak var instructionView: LoadingView!
override func viewDidLoad() {
super.viewDidLoad()
instructionView.randomTextIndexes = [1]
instructionView.delegate = self
}
...
}
// "instructionView" is the UIView outlet and "LoadingView" is the class
--This is the custom View Code ------
protocol LoadingViewDelegate {
func generated(random code:String)
}
class LoadingView: UIView {
var delegate:LoadingViewDelegate?
var randomTextIndexes:[Int] = []
}
I am getting EXC_BAD_ACCESS when I try to access the delegate as well as the randomTextIndexes from the viewDidLoad() method of the view controller. Could you please tell me what I am missing here.
My best guess is that you forgot to set the class of the custom view in the Interface Builder. Check the Identity Inspector for your object in the IB, it should look like this:
If it isn't set, then that's the problem.
I'm trying to do an lazy instantiation of IBOutlet var UITableView:
#IBOutlet lazy weak var tableView: UITableView? = {
return UITableView()
}()
But I'm getting the following errors:
<unknown>:0: error: cannot convert return expression of type 'UITableView?' to return type 'UITableView?'
<unknown>:0: error: cannot assign value of type 'UITableView?' to type 'UITableView??'
<unknown>:0: error: cannot assign value of type 'UITableView?' to type 'UITableView??'
Why I'm getting this error?
In this other case works just fine:
lazy var viewController: ViewController = {
return ViewController()
}()
Here's the right way to connect your ui elements to the Interface builder, take a look, it should help:
https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/ConnectTheUIToCode.html
What you are trying to do is impossible, and kind of senseless.
When you mark a property as an #IBOutlet and connect your code to your .storyboard or .xib, it will look like:
class MyViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
...
}
Notice, that tableView property does not have an initialiser. This is because it will not be initialised during MyViewController construction, but will be assigned later when interface builder will load the view. Also, this will only happen if you instantiate your view controller via storyboard or xib. Doing let viewController = MyViewController() will result in tableView property always being nil (unless you override your constructors or func loadView()).
lazy properties on the other hand MUST have an initialiser, which will be called lazily after you first access the property. Just like you've done in your code. The thing is, when you mark your tableView as lazy and provide your own initialiser for it, in runtime it will create an UITableView instance, which is not connected to your interface builder (.storyboard) in any way.
So you should either not use storyboards and make your layout programmatically (just like you did in your code, but removing #IBOutlet), or let the interface builder instantiate your views for you by removing your initialiser and lazy keyword.
I try to assign UIView super class object's to other same type class object, getting error my code is below:
class ViewTest: UIViewController {
#IBOutlet var txtName: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
var first = ViewFirst()
var second = ViewSecond()
func c() {
first = second
let datePicker = UIDatePicker()
txtName.inputView = datePicker
}
}
}
class ViewFirst: UIView {
}
class ViewSecond: UIView {
}
Why needed to type cast but txtName.inputView = datePicker working without any type cast
Thanks
This is wrong syntax. ViewSecond is not subclass or superclass to ViewFirst,even if you force cast the object like this:
first = second as! ViewFirst
you still get crash error.
So if ViewSecond is subclass to ViewFirst, just do force cast:
first = second as! ViewFirst
there will be well.
How cound I reply directlly? I am new...
Why txtName.inputView = datePicker without any cast type?
Because UIDatePicker is subClass of UIView! And then, the UIView is just a type define. So you have no need to do cast. For example:
func giveMeSomeView(view: UIView)
You can put it like UIButton, UIImageView.... But in the fuction, you have no idea which the exactlly view-Type, right? You just addSubview. Of course if you want to set something, you can have a implicit cast:
func giveMeSomeView(view: UIView) {
if let imageView = view as? UIImageView {
imageView.image = UIImage(string:"XXXX")
}
}
If there some word you could not understand, forgive it. My English is bad.
You should be more precise, do you have an error? Or which line doesn't work?
I guess the error is on this line :
first = second
You have to cast the object :
first = second as! ViewFirst
Please be more specific.
You can solve this problem bij making ViewSecond a subclass of ViewFirst instead of UIView.
For example:
class ViewFirst: UIView {
}
class ViewSecond: ViewFirst {
}
I am writing a custom table header view that can expand/collapse, I wrote a protocol for it like below:
protocol ExpandableHeadViewDelegate{
var expandStateReference : [String : Bool] { get set }
var tblVw : UITableView { get set }
func didTapActionFromHeadVw(_ view: ExpandableHeadView, tag: Int)
}
Here is my expandable head view class, what Im trying to achieve is when this view is tapped I will be able to call the methods that I need from the UITableView where expandableView is embedded:
class ExpandableHeadView: UIView {
var delegate : ExpandableHeadViewDelegate?
//set up tap recognizer
.
.
.
.
private func viewTapped {
if let delegate = delegate {
delegate.tblVw.reloadData()
}
}
}
My viewcontroller that utilizes this class is as below:
class PlayersViewController: UIViewController,UITableViewDelegate,
UITableViewDataSource, ExpandableHeadViewDelegate {
var expandedCells = [String : Bool]() //
#IBOutlet var tableVw: UITableView! // compile time error
}
The issue I am facing is even though I declare my 'var tableVw' in my delegate class, Xcode gives me an error:
Protocol requires property 'tableVw' with type 'UITableView'; do you
want to add stub?
Setting var expandedCells .. however sets properly. If I set my tableVw as non-IBOutlet it can compile, but I want my tableVw to be an IBOutlet property.
EDIT:
Adding a stub variable for tableVw and assigning my tableView IBOutlet to it works. But Im still curious if this can be achieved without using a stub variable
internal var tableVw: UITableView?
#IBOutlet var tableView: UITableView!
.
.
func viewDidload {
self.tableVw = self.tableView
}
Quite simply - declare the property in your protocol as UITableView!. The ! matters - a lot.
! makes a variable an optional, but an explicitly unwrapped one. This basically means, that the unwrapping is done for you, under the hood. You promise the compiler, that although this variable can contain a nil, you will make sure it will be set with a proper value before you try to access it. In case of IBOutlets this is done for you by the SDK.
I have an UIViewController
class WelcomeViewController: UIViewController
and an UIView
class SignUpView: UIView
Now I want to set in my WelcomeViewController delegate of SignUpView:
protocol SegueDelegate {
func runSegue(identifier: String)
}
class SignUpView: UIView { ... }
and connect it in
class WelcomeViewController: UIViewController, SegueDelegate {
how can I set in my WelcomeViiewController those delegate? When I'm trying to set:
override func viewDidLoad() {
SignUpView.delegate = self
}
it returns me
Instance member 'delegate' cannot be used on type 'SignUpView'
how can I find a solution?
You are trying to set delegate to a class. It should be an instance of the class i.e
let signUpView = SignUpView()
signUpView.delegate = self
What would be the point in doing that? If you want to navigate from one View to another, just add that Segue in Storyboard with an Identifier, so you can call self.performSegueWithIdentifier("IdentifierOfSegue", sender: self)
Create a weak property in SignUpView of that delegate(protocol) and name it other than delegate
then you can set and use it.
I agree with the developers saying "you can just do that via segue" but
the problem is you didn't declare a delegate var in the SignUpView class
so you can implement it in the signIn , if you declared it please write the line of code for me in a comment to check it
for now ...
I can suggest that you make a subview to be a parent class then override
which method you want to call
and you need to declare the delegate var as an optional (so you won't have
a memory cycle) like the following line ...
var delegate: SegueDelegate?
Let's solve this for people in need whom could need a solution when reading this issue:
In your UIView:
class SignUpView: UIView
you need to add:
var delegate : SegueDelegate?
Now, still in your class SignUpView, you need to add the function you want to delegate, just like this:
func runSegue(identifier: String) {
delegate?.runSegue(identifier)
}
This will call your delegate:
protocol SegueDelegate {
func runSegue(identifier: String)
}
Now, in your ViewController, you should have your SignUpView somewhere (created programmatically or linked through Storyboard / XIB).
In your viewDidLoadfunction, add: signUpView.delegate = self.
Don't forget to add SegueDelegatein your class heritage.