Implementing Protocol in Swift to Change UIView's Attribute - ios

I thought I wrote this correctly. I'm trying to get a value set in another viewcontroller (then stored in a singleton) to change the height of the rectangle in my BarGraphView, but my BarGraphView doesn't redraw when the value I'm passing in is changed...can anyone spot what I'm missing?
BarGraphViewController:
import UIKit
class BarGraphViewController: UIViewController, BarGraphViewDataSource {
#IBOutlet weak var barGraphView: BarGraphView! {
didSet {
barGraphView.datasource = self
}
}
#IBOutlet weak var testLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
var firstBarHeight: Int = 30 {
didSet {
firstBarHeight = SharkTankSingleton.instance.firstInvestorTotalasInt / 250
updateUI()
}
}
private func updateUI() {
barGraphView.setNeedsDisplay()
}
func firstBarHeightForBarGraphView(sender: BarGraphView) -> CGFloat? {
return CGFloat(firstBarHeight)
}
BarGraphView:
import UIKit
protocol BarGraphViewDataSource: class {
func firstBarHeightForBarGraphView(sender: BarGraphView) -> CGFloat?
}
#IBDesignable
class BarGraphView: UIView {
#IBInspectable var firstBarColor: UIColor = UIColor.blueColor()
weak var datasource: BarGraphViewDataSource?
override func drawRect(rect: CGRect) {
let firstBarHeight = datasource?.firstBarHeightForBarGraphView(self) ?? 0
let firstBar = CGRect(x: 200, y: 400, width: 30, height: firstBarHeight)
// firstBarColor.setFill()
var firstBarPath = UIBezierPath(rect: firstBar)
UIColor.redColor().setFill()
firstBarPath.fill()

Related

UIView: how does the appearance() proxy work?

I have created a simple custom UIView:
final class TestView: UIView {
var testColor: UIColor = .white {
didSet {
backgroundColor = testColor
}
}
}
Then I wrote this in my view controller:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var testView: TestView!
#IBOutlet weak var testView2: TestView!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
TestView.appearance().testColor = .red
}
}
}
By doing this, I get an error:
Could you help me understanding what's wrong here and how to implement the UIAppearance proxy for any custom UIView?
Thank you for your help
You need to make the property #objc and dynamic:
final class TestView: UIView {
#objc dynamic var testColor: UIColor? = .white {
didSet {
backgroundColor = testColor
}
}
}
Worth noting: the UIAppearance proxy does NOT affect views which are already part of the view hierarchy.
So, in your example, adding #objc dynamic to your property will get rid of the crash, but you will not see any change to the two #IBOutlet views.
If you call it as part of viewDidLoad() (instead of DispatchQueue.main.asyncAfter):
override func viewDidLoad() {
super.viewDidLoad()
TestView.appearance().testColor = .red
}
The two #IBOutlet views will get the red background.
Or, if you add a new view to the hierarchy, it will get the red background:
class AppearanceTestViewController: UIViewController {
#IBOutlet weak var testView: TestView!
#IBOutlet weak var testView2: TestView!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
TestView.appearance().testColor = .red
self.addAnotherTestView()
}
}
func addAnotherTestView() -> Void {
let v = TestView()
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
NSLayoutConstraint.activate([
v.widthAnchor.constraint(equalToConstant: 240.0),
v.heightAnchor.constraint(equalTo: v.widthAnchor),
v.centerXAnchor.constraint(equalTo: view.centerXAnchor),
v.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
// this newly added view WILL have a red background
}
}

Importing animation function from custom swift file failed

I was creating animations for buttons using extension in swift but when i call the animation function it generates error.
import UIKit
extension UIButton{
func wiggle() {
let wiggleAnim = CABasicAnimation(keyPath: "psoition")
wiggleAnim.duration = 0.05
wiggleAnim.repeatCount = 5
wiggleAnim.autoreverses = true
wiggleAnim.fromValue = CGPoint(x: self.center.x - 4.0, y: self.center.y)
wiggleAnim.toValue = CGPoint(x: self.center.x + 4.0, y: self.center.y)
layer.add(wiggleAnim, forKey: "position")
}
}
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var colorizeBtn: UIButton!
#IBOutlet weak var wiggleBtn: UIButton!
#IBOutlet weak var dimBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func colorizeBtnWasPressed(_ sender: Any) {
}
#IBAction func wiggleBtnWasPressed(_ sender: Any) {
wiggleBtn.wiggle()
}
#IBAction func dimBtnwasPressed(_ sender: Any) {
}
}
View Controller
Your wiggleBtn is of type UIView but you write an extension to UIButton.
Either change the extension to UIView or change the type of wiggleBtn to UIButton.

UITextField without get property swift 4

I have custom control which contains textField. Sample code looks like this:
#IBDesignable
class MyTextField: UIView {
private var _text: String?
private var textField: UITextField?
#IBInspectable var text: String? {
get {
return _text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
//updateView()
}
}
override func awakeFromNib() {
super.awakeFromNib()
updateView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
updateView()
}
func updateView() {
textField = UITextField(frame: CGRect(x: 0, y: 0, width: 97, height: 30))
self.addSubview(textField!)
textField!.text = self._text
}}
When I create an outlet to my custom control in code I can set text property, but I can't get it. What am I doing wrong ? I'm new in this so I don't understand...
EDIT:
Add MyTextField to Main.storyboard and standard textField
Create an outlets ​#IBOutlet var myTextField: MyTextField! and
standardTextField
Set Text property of my control in Object Inspector
My control on storyboard updates correctly and "test text" is visible
Add a button and create an action outlet
case 1: works fine
#IBAction func click(_ sender: UIButton) {
myTextField.text = standardTextField.text
}
case 2: doesn't work
#IBAction func click(_ sender: UIButton) {
standardTextField.text = myTextField.text
}
After case 2, in standardTextField is only value which i set from object inspector. If i change value of myTextField.text, standardTextField still shows a orginal value from object inspector
I replicated your setup in Xcode. Case 2 does work — the standardTextField displays MyTextField's text when the button is clicked.
Note: in MyTextField I assumed that you want to create textField once, instead of recreating it each time the text updates. Case 2 was working without this change, though.
import UIKit
class ViewController: UIViewController {
#IBOutlet var myTextField: MyTextField!
#IBOutlet var standardTextField: UITextField!
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: #selector(click(_:)), for: .touchUpInside)
}
#IBAction func click(_ sender: UIButton) {
// myTextField.text = standardTextField.text // Case 1 works
standardTextField.text = myTextField.text // Case 2 works
}
}
MyTextField control:
import UIKit
#IBDesignable
class MyTextField: UIView {
private var _text: String?
private var textField = UITextField(frame: CGRect(x: 0, y: 0, width: 97, height: 30))
#IBInspectable var text: String? {
get {
return _text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
updateView()
}
}
override func awakeFromNib() {
super.awakeFromNib()
self.addSubview(textField)
updateView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.addSubview(textField)
updateView()
}
func updateView() {
textField.text = self._text
}
}
A simple solution for not working getter is change text property
from:
#IBInspectable var text: String? {
get {
return _text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
updateView()
}
}
to
#IBInspectable var text: String? {
get {
return textField!.text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
updateView()
}
}
But I don't know if this is correct...
Does it make any sense ?

Swift objects won't update after delegate called

I want a stepper and label to reset to zero after my variable in another class is also reset. The variables reset but the stepper and label do not even after using a delegate.
View Controller:
class ViewController: UIViewController, CircleViewDelegate {
var colors = CircleView()
#IBOutlet weak var circleView1: CircleView!
#IBOutlet weak var redStepper: UIStepper!
#IBOutlet weak var redValue: UILabel!
#IBAction func stepperChange(sender: UIStepper)
{
circleView1.redd1 = Int(redStepper.value);
redValue.text = Int(sender.value).description;
}
func updateRedStepperValue(value: Double) {
redStepper.value = value
redValue.text = Int(colors.redd1.value).description;
}
override func viewDidLoad() {
super.viewDidLoad()
colors.delegate = self
}
}
CircleView:
protocol CircleViewDelegate
{
func updateRedStepperValue(value: Double)
func updateGreenStepperValue(value: Double)
func updateBlueStepperValue(value: Double)
}
class CircleView: UIView
{
var delegate: CircleViewDelegate?
var redd1 = 0
func updateValues()
{
if(redd1==Int(red1))
{
redd1=0;
delegate?.updateRedStepperValue(0.0)//
}
}
}
The problem is that your making a brand new instance of your CircleView.
let cycle = CircleView()
You need to set your delegate to your current working instance.
To do so, you should replace your assignment in your viewDidLoad with the following:
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.sharedApplication().delegate! as! AppDelegate
if let viewControllers = app.window?.rootViewController?.childViewControllers {
viewControllers.forEach { vc in
if let cont = vc as? CircleView {
cont.delegate = self
}
}
}
}
Here's an article with project files.

Immutable Value of Type 'ShopDisplay' Only Has Mutating Members Named 'setAllShopItems'

I'm trying to set an array property that is a var not a let. Notice the setAllShopItems method:
struct ShopDisplay {
private var allShopItemCategories: [ShopItemCategory]
private var currentShopItemCategory: ShopItemCategory
private var shopItemCategoryIcon: MMImageView
private var currentShopItemGender: Gender
private var allShopItems: [ShopItem]
private var allDisplayedShopItems: [ShopItem]
init() {
allShopItemCategories = [ShopItemCategory]()
currentShopItemCategory = ShopItemCategory(rawValue: "TO")!
shopItemCategoryIcon =
MMImageView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
currentShopItemGender = .Female
allShopItems = []
allDisplayedShopItems = []
}
mutating func setShopItemsToDisplay() {
allDisplayedShopItems.removeAll()
for item in self.allShopItems {
let itemCategory = item.object["category"] as! String
if itemCategory == currentShopItemCategory.rawValue {
allDisplayedShopItems.append(item)
}
}
}
mutating func setAllShopItems(shopItems: [ShopItem]) {
self.allShopItems = shopItems
}
I call this setAllShopItems() method like so:
override func prepareForSegue( segue: UIStoryboardSegue,
sender: AnyObject?) {
if (segue.identifier == "dressingRoom") {
let dressingRoomViewController = segue.destinationViewController
as! DressingRoomViewController
dressingRoomViewController.getShopDisplay().setAllShopItems(self.allShopItems)
dressingRoomViewController.getShopDisplay().setAllShopItemCategories(
self.categories)
}
}
the property returned by getShopDisplay is also a var:
class DressingRoomViewController: UIViewController,
UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource
{
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
private let identifier = "cellIdentifier"
private let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
private let cellSpacing: CGFloat = 5
private let cellsPerRow: CGFloat = 5
private var cellSize: CGFloat = 0.00
private var shopDisplay = ShopDisplay()
#IBOutlet weak var categoryIcon: MMImageView!
func getShopDisplay() -> ShopDisplay {
return self.shopDisplay
}
So if all of these variable being set are vars, and I use the keyword mutating whenever I am setting them if they are structs, how I am still getting this error?:
/Users/Ben/Documents/Development/MirrorMirror/MirrorMirror/ChooseShopViewController.swift:40:44:
Immutable value of type 'ShopDisplay' only has mutating members named
'setAllShopItems'

Resources