Cannot convert value of type '() -> _' to specified type UIImageView
Asked Answered
C

3

19

I am trying to make a UIImage view clickable, but I am having no luck. What is the best way to complete this task? The error I am getting is "Cannot convert value of type '() -> _' to specified type 'UIImageView'".

lazy var profileImageView: UIImageView = {
     let imageView = UIImageView()
     imageView.image = UIImage(named: "ic_file_upload_white_48pt")
     imageView.translatesAutoresizingMaskIntoConstraints = false
     imageView.contentMode = .scaleAspectFill

     imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSelectorProfileImage)))
     imageView.isUserInteractionEnabled = true
     return imageView
 }
Confidence answered 10/3, 2017 at 23:21 Comment(2)
try deleting the lazy declaration and add () after the closing bracketTattle
Because that's a variable, not a function with a returning parameter, of type UIImageView. You should create a handler with a return type.Rill
P
6

Looks like a syntax issue, try

func profileImageView() -> UIImageView {
        let imageView = UIImageView()
        imageView.image = UIImage(named: "ic_file_upload_white_48pt")
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFill

        imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSelectorProfileImage)))
        imageView.isUserInteractionEnabled = true
        return imageView
}
Privacy answered 10/3, 2017 at 23:27 Comment(1)
Using a lazy var is perfectly valid, but it's missing parentheses after the closure.Undergrowth
U
67

You are telling the compiler that you want to make profileImageView contain a closure. If you want profileImageView to contain the results of that closure, you need to add parens after in order to invoke the closure:

lazy var profileImageView: UIImageView = {
  //your code here
  return imageView
}()

Note the parentheses after the closure. That assigns the result of calling the closure to your variable profileImageView the first time you reference the variable.

Edit:

Any time you see a type of (<something>) -> type it's a closure. The -> bit separates the parameters from the return type. Swift's error messages can be hard to decipher, but that's a clue that you're returning a closure rather than whatever is expected.

Edit #2:

Note that there are 2 similar constructs related to a variable defined by a closure in Swift: Computed properties and lazy variables.

Computed properties

A computed property is declared as

var computed: type { closure_returning_result }

There is no equals sign in a computed property. Every time you ask for a value from the computed property, the closure code runs, and the returned value of the closure is the new value of the property.

Lazy vars:

A lazy var looks like this:

lazy var lazy: type = expression_returning_result

The expression is often a closure, but it doesn't have to be. A common form of a lazy var would use a closure, like this:

lazy var lazy: type = { closure_returning_result }()

There is an equals sign in the declaration of a lazy var. If you use a closure to assign a value to a lazy var, you need to add parentheses after the closure, so the lazy var is assigned the returned value of the closure, not the closure itself. This thread came up when @jameel forgot the closing parentheses in his code.

Consider the following code:

var counter = 1

var computed: Int  {
    counter += 1
    return counter
}

lazy var lazy: Int = {
    counter += 1
    return counter
}()

print("lazy = \(lazy)")
print("lazy = \(lazy)")
print("lazy = \(lazy)")

print("computed = \(computed)")
print("computed = \(computed)")
print("computed = \(computed)")

That prints the output:

lazy = 2
lazy = 2
lazy = 2
computed = 3
computed = 4
computed = 5

Note that the value of the lazy var doesn't change, but the value of the computed property does. That is the key difference between them.

A lazy variable gets evaluated once the first time you ask for it, and that value "sticks". The expression/closure that gives the value is not run until you ask for it, and then only once.

In contrast, a computed property always uses a closure, and every time you ask for the value of a computed property, the closure is executed.

Edit #3: (9/28/2023)

Also note that the fact that a lazy variable isn't initialized until you first reference it can have consequences.

If you reversed the order of the test code above:

print("computed = \(aFoo.computed)")
print("computed = \(aFoo.computed)")
print("computed = \(aFoo.computed)")

print("lazy = \(aFoo.lazy)")
print("lazy = \(aFoo.lazy)")
print("lazy = \(aFoo.lazy)")

print("computed = \(aFoo.computed)")

print("lazy = \(aFoo.lazy)")

print("computed = \(aFoo.computed)")

The output would be

computed = 2
computed = 3
computed = 4
lazy = 5
lazy = 5
lazy = 5
computed = 6
lazy = 5
computed = 7

With that sample code, we don't reference lazy until after we've invoked our computed property 3 times. As soon as we reference lazy, it increments counter and saves that result as its new value, FOREVER.

If we then reference computed again, it again increments counter.

lazy's value is stuck at 5 for the lifetime of this execution of the code.

Undergrowth answered 10/3, 2017 at 23:39 Comment(0)
P
6

Looks like a syntax issue, try

func profileImageView() -> UIImageView {
        let imageView = UIImageView()
        imageView.image = UIImage(named: "ic_file_upload_white_48pt")
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFill

        imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSelectorProfileImage)))
        imageView.isUserInteractionEnabled = true
        return imageView
}
Privacy answered 10/3, 2017 at 23:27 Comment(1)
Using a lazy var is perfectly valid, but it's missing parentheses after the closure.Undergrowth
B
6

You should add paranthesis after the end of computed property in order to execute it.

lazy var profileImageView: UIImageView = {
     let imageView = UIImageView()
     imageView.image = UIImage(named: "ic_file_upload_white_48pt")
     imageView.translatesAutoresizingMaskIntoConstraints = false
     imageView.contentMode = .scaleAspectFill

     imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSelectorProfileImage)))
     imageView.isUserInteractionEnabled = true
     return imageView
 }()
Bicarbonate answered 3/6, 2019 at 14:19 Comment(1)
Well, yeah. I said that too, over a year ago, but I also explained WHY.Undergrowth

© 2022 - 2024 — McMap. All rights reserved.