Set property of IBOutlet - ios

I created an outlet by draggin & dropping an UIImageView into the ViewController.swift file
#IBOutlet weak var imageView: UIImageView!
I want to set the property of imageView to
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
Doing this
#IBOutlet weak var imageView: UIImageView! {
self.imageView.contentMode = .scaleToFill
self.imageView.clipsToBounds = true
}
XCode complains that
#IBOutlet attribute requires property to be mutable
How do I go on about this?

What you're trying to do is a perfectly acceptable way to set up an #IBOutlet's properties… just don't reference other #IBOutlets, or the main view, etc, as this may force the main view to load earlier than expected.
You just need to wrap your code inside a didSet block…
#IBOutlet weak var imageView: UIImageView! {
didSet {
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
}
}

An outlet is basically just a link to that UI element in the storyboard.
So you link your UIImageView in the storyboard to an element in your UIViewController code and then you can access and change properties of that element once it is loaded.
The Problem
This is the wrong syntax for doing this:
#IBOutlet weak var imageView: UIImageView! {
self.imageView.contentMode = .scaleToFill
self.imageView.clipsToBounds = true
}
The reason for the following error
#IBOutlet attribute requires property to be mutable
is.. having a closure after a property declaration acts as a getter. As an example I can do this
let two = 2
let three = 3
var five: Int {
return two + three
}
I cannot set any of the above values. two and three are let only (read only constants) and five is a computed property, so I can also only read that. I wont complicate the matter further but I would highly recommend giving this part of the documentation a read when you can.
Solution
You should have an outlet only:
#IBOutlet weak var imageView: UIImageView!
and then somewhere else in the code you can do things with it.
I think in your case it would be best on viewDidLoad. ViewDidLoad is called after the view and all of its outlets and properties have been set. So this is a safe place to start to use your outlets to further configure your view.
override func viewDidLoad() {
super.viewDidLoad()
self.imageView.contentMode = .scaleToFill
self.imageView.clipsToBounds = true
}

First of all, it's not good approach to override outlet's getter.
Secondly, technically you can override getter and setter of outlet.
In your case you defined getter, and forgot about setter. So you got get only variable.
private var _someView: UIImageView!
#IBOutlet weak var someView: UIImageView! {
get {
self.someView.contentMode = .scaleToFill
self.someView.clipsToBounds = true
return _someView
}
set {
_someView = newValue
}
}
You should use private variable to avoid recursion in getter.

Just try in this way and invoke this method inside viewDidLod()
func createRoundGreenImage(imageView : UIImageView, with image :
String) {
self.imageView.contentMode = .scaleToFill
self.imageView.clipsToBounds = true
}

Related

Change image color with tapGesture on different viewController

I am trying to get the UIImageView wCircle to change to red when the UIImageView on a different viewController rDot is tapped. The problem is, when I tap rDot I get the error Thread 1: EXC_BAD_INSTRUCTION (code = EXC_I386_INVOP subcode = 0x0) I made wCircle a global variable so it could be reached in the other viewController.
First viewController
weak var wCircle: UIImageView!
class SecondViewController: UIViewController {
#IBOutlet weak var wCircle: UIImageView!
}
Second viewController
class ProgressViewController: UIViewController {
#IBOutlet weak var rDot: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
rDot.isUserInteractionEnabled = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(rdotimageTapped(tapGestureRecognizer:)))
rDot.addGestureRecognizer(tapGestureRecognizer)
}
func rdotimageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
wCircle.image = wCircle.image!.withRenderingMode(.alwaysTemplate) //error on this line
wCircle.tintColor = UIColor.red
}
}
you code is not organized and indicate nothing Any way
change this line of code
replace weak var wCircle: UIImageView!
with
weak var wCircle = UIImageView()
and this
func rdotimageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
wCircle?.image = whiteDot.image!.withRenderingMode(.alwaysTemplate) //error on this line
wCircle?.tintColor = UIColor.red
}
wCircle is weak variable and it is optional value.So when you are calling it from other class it can't be optional. Also make it strong variable

Initialise font property at IBOutlet

Is there a way to pre-set the text property of a label at it's outlet? below is what I have in mind but it doesnt work because it is the wrong syntax
#IBOutlet weak var commentHeaderLbl: UILabel! {
didSet {
self.font = UIFont.systemFontOfSize(8)
}
}
If your label is in a UIViewController subclass, you could just do this at viewDidLoad.
If your label is in a UITableViewCell subclass, you could just do this at awakeFromNib.
Example:
override func awakeFromNib() {
super.awakeFromNib()
self.commentHeaderLbl.font = UIFont.systemFontOfSize(8)
}
If You are using interface builder, it is possible to define the font size there too.
Try this:
#IBOutlet weak var commentHeaderLbl: UILabel! {
didSet {
self.font = UIFont.systemFontOfSize(8, weight: UIFontWeightThin)
}
}
you can change the weight property as per your need.

How do you make a function which sets properties of other instances in swift?

I am trying to write the following code in a shorter way.
func colourChangeIfDeletionCancelled(word:Int){
for i in 0...selectedWords[word].count - 1 {
let square = selectedWords[word][i]
arrayOfRows[square.0][square.1].topToRight.backgroundColor = nil
arrayOfRows[square.0][square.1].topToLeft.backgroundColor = nil
arrayOfRows[square.0][square.1].bottomToRight.backgroundColor = nil
arrayOfRows[square.0][square.1].bottomToLeft.backgroundColor = nil
arrayOfRows[square.0][square.1].horizontalTube.backgroundColor = nil
arrayOfRows[square.0][square.1].verticalTube.backgroundColor = nil
arrayOfRows[square.0][square.1].endFromLeft.backgroundColor = nil
arrayOfRows[square.0][square.1].endFromRight.backgroundColor = nil
arrayOfRows[square.0][square.1].endFromTop.backgroundColor = nil
arrayOfRows[square.0][square.1].endFromBottom.backgroundColor = nil
}
}
This code works but I am sure there is a better (shorter) way to do write it but I am unsure how. I have tried to make a function that takes the subviews as a variable but am lost on how to do that. I'm sure this isn't the hardest problem but I am stuck on it and any help is appreciated.
Edit:
arrayOfRows is just an array of arrays of a class I have created called LetterSquareView
class LetterSquareView: UIView {
var letter:String!
#IBOutlet weak var topToLeft: UIView!
#IBOutlet weak var topToRight: UIView!
#IBOutlet weak var bottomToRight: UIView!
#IBOutlet weak var bottomToLeft: UIView!
#IBOutlet weak var horizontalTube: UIView!
#IBOutlet weak var verticalTube: UIView!
#IBOutlet weak var endFromLeft: UIView!
#IBOutlet weak var endFromRight: UIView!
#IBOutlet weak var endFromTop: UIView!
#IBOutlet weak var endFromBottom: UIView!
#IBOutlet weak var topToLeftWhiteSpace: UIView!
#IBOutlet weak var topToRightWhiteSpace: UIView!
#IBOutlet weak var bottomToRightWhiteSpace: UIView!
#IBOutlet weak var bottomToLeftWhiteSpace: UIView!
#IBOutlet weak var letterSquareViewView: LetterSquareViewView!
#IBOutlet var letterSquareView: UIView!
#IBOutlet weak var letterLbl: UILabel!
init(frame: CGRect, letter: String) {
super.init(frame: frame)
NSBundle.mainBundle().loadNibNamed("LetterSquareView", owner: self, options: nil)
letterLbl.text = letter.capitalizedString
self.letter = letter
self.addSubview(letterSquareView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The subviews that I am trying to change the background colour of have a background colour set when they are created. I am trying to delete that colour when they are deleted. I thought there would be a function where you could input an array of the views and input a property of there's to set and it'd just be set but I can't find anything of the sort.
I guess the most straight forward way is to have:
func clearBackgroundColors(e: WhateverViewThisIs) {
for view in [e.topToRight, e.topToLeft, etc. ] {
view.backgroundColor = nil
}
}
clearBackgroundColors(arrayOfRows[square.0][square.1])
A good question to think is about responsibilities of the classes. I would argue the view in arrayOfRows[][] should have it's subviews private, knowledge of them is likely an implementation detail and not of public knowledge. So the clearBackgroundColors() method should be placed there instead of setting all colors from a more global place.
import UIKit
var arrayOfRows: [[UIView]] = [[]]
let square: (Int, Int)!
A way to make your touple code cleaner:
//extracting touple
let (row, col) = square
arrayOfRows[row][col].topToRight.backgroundColor = nil
Only use this is you are sure all the subviews of this view you want to make the background color nil.
//for all views
for arrayOfColumns in arrayOfRows {
for views in arrayOfColumns {
for subview in views.subviews {
subview.backgroundColor = nil
}
}
}
This is how I recommend to handle it. This also allows you to customize those views further with OOP design. Then you can
//with class type
//use this if there are other subviews
//make all your views in your list a CustomViewToMakeNil
class CustomViewToMakeNil: UIView {
//set the views content
}
for arrayOfColumns in arrayOfRows {
for views in arrayOfColumns {
for subview in views.subviews {
//check if they are of the type you want to make the color nil
if subview is CustomViewToMakeNil {
subview.backgroundColor = nil
}
}
}
}

UIView doesn't center properly in UIScrollView with autolayout

I'm making an application where I need an x amount of custom UIView and place them in a scrollView. The layout of the scrollView and the UIView xib are set with AutoLayout. The result I'm getting now is this:
First View is well centred in ScrollView
Second View is wrong and has a lot of space in between
See my VC Code under here.
let sponsors = Sponsors.createSponsors() <-- Array
override func viewDidLoad() {
super.viewDidLoad()
configureSponsors()
}
//MARK: Load the AddView in ScrollView
func configureSponsors() {
self.scrollView.contentSize = CGSizeMake(CGFloat(sponsors.count) * self.scrollView.frame.size.width, self.scrollView.frame.size.height)
for sponsor in sponsors {
numberOfItems++
let addView = NSBundle.mainBundle().loadNibNamed("AddView", owner: self, options: nil).last as! AddView
addView.addDataToAddView(sponsor)
addView.frame = CGRectMake(CGFloat(numberOfItems - 1) * scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height)
self.scrollView.addSubview(addView)
}
}
And here is my UIView code:
//MARK: Outlets
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var sponsorTitle: UILabel!
#IBOutlet weak var sponsorLogo: UIImageView!
#IBOutlet weak var sponsorSubtitle: UILabel!
#IBOutlet weak var roundView: UIView!
#IBOutlet weak var playMusicButton: UIButton!
//MARK: Properties
var cornerRadius: CGFloat = 3.0
func addDataToAddView(sponsor: Sponsors) {
backgroundImageView.image = UIImage(named: sponsor.backgroundImage)
sponsorLogo.image = UIImage(named: sponsor.logoImage)
sponsorTitle.text = sponsor.title
sponsorSubtitle.text = sponsor.subTitle
}
override func layoutSubviews() {
roundView.layer.cornerRadius = roundView.frame.size.width / 2
roundView.alpha = 0.7
roundView.clipsToBounds = true
}
//MARK: PlayVideo
#IBAction func playVideo(sender: UIButton) {
//play music
}
I've already searched for the same problems. I found out that I have to use the Pure Auto Layout Approach. Should that mean I need to programatically set the constraints for the UIView?
Many thanks,
Dax
Update:
Pictures for scrollView setup:
You should not perform layout calculations in viewDidLoad as frames are not set correctly till then.
Correct place to do this is either viewWillLayoutSubviews or viewDidLayoutSubviews as you will get the correct frame data when these functions are called

ScrollView unable to display image which passed from another VC

I need to display the image pass in from the another ViewController
first,I have tested using only ImageView to display the image and it works as below:
Thanks. Your help is greatly appreciated.
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var TempImgView: UIImgeView!
var passInImg: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
TempImgView.image = passInImg!
}
When I use UIScrollView ( for scrolling the large passInImg) , below code not working
Probelm:
error : fatal error: unexpectedly found nil while unwrapping an
optional value
But I have tested the pass in image in the above code.
what need to be done with the scrollView?
Should be used in viewDidLoad?
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var myUIScrollView: UIScrollView!
var myImgView: UIImageView!
var passInImg: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
self.myUIScrollView.maximumZoomScale = 10.0
self.myUIScrollView.minimumZoomScale = 1.0
self.myUIScrollView.delegate = self
myImgView.image = passInImg!
self.myUIScrollView.addSubview(myImgView)
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return myImgView
}
First check if myUIScrollView is connected to an outlet in storyboard and always check before using an optional variable:
if let img = passInImg {
myImgView.image = img
}
The difference is the way you are declaring the UIImageView.
In the first case:
#IBOutlet weak var TempImgView: UIImageView!
TempImgView is an #IBOutlet and hence initialised whenever the storyboard view/xib is loaded.
But then when you are using the scrollview, you have declared the UIImageView as a class variable and not #IBOutlet.
var myImgView: UIImageView!
It has to initialised before you try to access it.
Try:
myImgView = UIImageView()
myImgView.image = passInImg!
Please initialize the myImgView with frame before set the image.
It return nil because the myImgView not initalize
myImgView = UIImageView(frame:CGRectMake(10, 50, 100, 300));
It works for me. Hope it will helps you.Thanks alot

Resources