【问题标题】:UILabel doesn't update after button click按钮单击后UILabel不更新
【发布时间】:2017-04-25 17:44:10
【问题描述】:

后台 Xcode 版本 8.2 (8C38)
ios 10.2 for iPhone 7 plus - 模拟器

我在objective-c中得到了这个工作,我现在正试图让它在swift中工作。代码编译并运行,但单击按钮时标签不会更改。如果标签正在更新,我会将其发布在 CR 上而不是此处。

一开始是面试带回家的问题,但客观 c 部分让我参加了现场面试。我正在追求这个以了解更多关于 swift 的信息。面试题

/*
 * Create a static library or iOS Framework using Objective-C that performs the following 3 functions:
 * - Collects the GPS location (latitude and longitude) of the user at a point in time
 * - Collects the battery state and returns whether or not the device is plugged in and what percentage of life is left.
 * - Accesses any publicly available, free API to collect the data of your choice and returns it
 *        (this should be a network call)
 *
 * Build a simple application with 3 buttons and a label where text can be displayed.  Each button should call into the
 * three functions of the library described above and output the response to the label.  Your application should consist 
 * of two tabs, one written in Objective-C and one written in Swift.  Both tabs should call into the same Objective-C
 * library and perform the same function. 
 *
 * Only use Apple frameworks to complete this task. Fully comment your code explaining your logic and choices where multiple
 * choices are available. For example, Apple provides numerous ways to retrieve a network resource, document why you choose
 * the solution you did.
 */

这可能是一个重复的问题,但我不确定我是否看过 以下问题:

我不确定问题出在我进行的异步调用、按钮本身的创建还是顶部的属性/var 声明中。

import UIKit

class ViewController: UIViewController {
    var getGPSLongitudeAndLatitudeWithTimeStamp : UIButton?
    var getBatteryLevelAndState : UIButton?
    var getNextorkImplementation : UIButton?
    var displayButtonAction : UILabel?

    // The following variables are used in multiple functions. They are constant during the display of the super view
    // and control the size of the subviews
    var selfWidth : CGFloat = 0.0
    var buttonHeight : CGFloat = 0.0
    var viewElementWidth : CGFloat = 0.0
    var buttonYCenterOffset : CGFloat = 0.0       // The y center should be half the value of the height
    var buttonXCenter : CGFloat = 0.0             // Center the button title relative to the width of the button and the width of the super view
    var buttonXInit : CGFloat = 0.0
    var buttonVerticalSeparation : CGFloat = 0.0
    var startingVerticalLocation : CGFloat = 0.0
    var displayLabelHeight: CGFloat = 50.0

    func initFramingValuesOfMyDisplay() {
        selfWidth = self.view.bounds.size.width
        buttonHeight = 20.0               // This should be programmable in relative to self.view.bounds.size.height
        viewElementWidth = 0.8 * selfWidth;
        buttonYCenterOffset = buttonHeight / 2.0; // The y center should be half the value of the height
        buttonXCenter = selfWidth / 2.0;   // Center the button title relative to the width of the button and the width of the super view
        buttonXInit = 0.0;
        buttonVerticalSeparation = buttonHeight + buttonYCenterOffset;
        startingVerticalLocation = 430.0;  // 430 was chosen based on experimentation in the simulator
    }

    func setLabelWithGPSLatitudeAndLongitudeWithTimeStampData()
    {
        var actionString : String = "Testing Label Text"

        actionString = "GPS Button Action Failure: Data Model not created";

        DispatchQueue.global().async {
            self.displayButtonAction?.text = actionString
        }
    }

    func setLabelWithBatteryLevelAndState() {
        var actionString : String = "Get Battery Level and State";

        actionString = "Battery Button Action Failure: Data Model not created"

        DispatchQueue.global().async {
            self.displayButtonAction?.text = actionString
        }
    }

    func setLabelActionNetwork() {
        var actionString :String = "Fake Button set to American Express Stock Price";

        actionString = "Network Button Action Failure: Data Model not created";

        DispatchQueue.global().async {
            self.displayButtonAction?.text = actionString
        }
    }

    func makeAButton(yButtonStart : CGFloat, buttonTitle: String, underSubview: UIButton?) -> UIButton
    {
        let thisButton = UIButton.init(type: .system)
        thisButton.frame = CGRect(x: buttonXInit, y: yButtonStart, width: viewElementWidth, height: buttonHeight)
        thisButton.setTitle(buttonTitle, for:UIControlState.normal)

        thisButton.backgroundColor = UIColor.yellow
        thisButton.setTitleColor(UIColor.black, for: UIControlState.normal)
        if ((underSubview) == nil) {
            self.view.addSubview(thisButton)
        }
        else {
            self.view.insertSubview(thisButton, belowSubview:underSubview!)
        }

        return thisButton;
    }

    func makeALabel(yLabelStart : CGFloat, underSubview: UIButton?) -> UILabel
    {
        let thisLabel = UILabel.init()
        thisLabel.frame = CGRect(x: selfWidth * 0.1, y: yLabelStart, width: viewElementWidth, height: displayLabelHeight)

        thisLabel.font = thisLabel.font.withSize(12)
        thisLabel.lineBreakMode = .byWordWrapping;
        thisLabel.numberOfLines = 0;
        thisLabel.textAlignment = NSTextAlignment.center;
        thisLabel.textColor = UIColor.black;

        if ((underSubview) == nil) {
            self.view.addSubview(thisLabel)
        }
        else {
            self.view.insertSubview(thisLabel, belowSubview:underSubview!)
        }

        return thisLabel;
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.view.backgroundColor = UIColor.white
        initFramingValuesOfMyDisplay()
        addButtonAndLabels()
    }

    func addButtonAndLabels() -> Void {
        if (selfWidth < 1.0) {
            return;
        }
        var viewElementVerticalLocation: CGFloat = startingVerticalLocation;
        // var touchUpInside : UIControlEvents = touchUpInside;

        let getGPSLongitudeAndLatitudeWithTimeStamp : UIButton = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get GPS Location with TimeStamp", underSubview: nil)
        getGPSLongitudeAndLatitudeWithTimeStamp.addTarget(self, action: #selector(setLabelWithGPSLatitudeAndLongitudeWithTimeStampData), for:  .touchUpInside)
        viewElementVerticalLocation += buttonVerticalSeparation

        let getBatteryLevelAndState : UIButton = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get Battery Level and State", underSubview: getGPSLongitudeAndLatitudeWithTimeStamp)
        getBatteryLevelAndState.addTarget(self, action: #selector(setLabelWithBatteryLevelAndState), for:  .touchUpInside)
        viewElementVerticalLocation += buttonVerticalSeparation

        let getNextorkImplementation : UIButton = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get Battery Level and State", underSubview: getBatteryLevelAndState)
        getNextorkImplementation.addTarget(self, action: #selector(setLabelWithBatteryLevelAndState), for:  .touchUpInside)
        viewElementVerticalLocation += buttonVerticalSeparation

        let displayButtonAction = makeALabel(yLabelStart: viewElementVerticalLocation, underSubview: getNextorkImplementation)
        displayButtonAction.text = "No Action Yet"
    }

    required init(coder aDecoder: NSCoder) {
        super.init(nibName: nil, bundle: nil)
        initFramingValuesOfMyDisplay()
    }
}

【问题讨论】:

    标签: ios swift asynchronous


    【解决方案1】:

    您的displayButtonAction 实际上是零。请注意,在您的方法 addButtonAndLabels() 中,您正在创建一个标签并仅在该方法的范围内填充文本:

    let displayButtonAction = makeALabel(yLabelStart: viewElementVerticalLocation, underSubview: getNextorkImplementation)

    在您的makeALabel 方法中,您将该标签作为视图的子视图插入,这就是为什么它在您运行应用程序时出现的原因。您没有将该标签分配给displayButtonAction。

    总之,您正在创建一个本地范围 UILabel,填充它的占位符文本,将其作为视图的子视图插入,然后丢弃它,然后尝试在按下按钮时填充 displayButtonAction 标签的文本这是nil。

    在addButtonAndLabels() 内,通过以下方式将实例化标签分配给视图控制器主范围内的主标签:

    self.displayButtonAction = displayButtonAction

    会让你朝着正确的方向开始。

    在这种情况下使用调试器工具、断点和po 是你的朋友。

    【讨论】:

    • displayButtonAction 没有设置为 makeALabel 的返回值?
    • 您正在返回一个标签,但您没有将该标签分配给您的 displayButtonAction 标签。
    • 谢谢,解决了。 3个按钮也存在问题,它们都是局部变量而不是self的成员。
    【解决方案2】:

    我注意到的主要事情是您正在尝试在后台队列中设置标签的文本。除了主队列之外,您永远不应该对任何内容进行 UI 更改,因此将其更改为主队列,如下所示:

    DispatchQueue.main.async {
            self.displayButtonAction?.text = actionString
        }
    

    您也应该在调试器中收到警告,告诉您这一点。

    您还应该在该函数中抛出一个断点,以验证 displayButtonAction 不是 nil,并且它有一个 frame,它实际上在屏幕上并且具有非零大小。充分利用这些调试器。

    【讨论】:

    • 过去 3 小时都在调试器中。标签的默认文本确实出现了,只是永远不会更新。调试器中的 displayButtonAction 不是 nil。
    • @pacmaninbw 好的,那么将它放回主队列是否可以解决问题?无论如何,这必须发生。
    • 之前让 main 再次尝试过。会尽快回复您。
    • 不,它没有帮助,但它可能发现了代码中的问题,我现在在控制台中收到大量消息。
    • 感谢您的帮助!我还在学习调试器,你说的很对。
    【解决方案3】:

    您无法在后台(异步)线程上更新 UI 元素。您需要获取 main 线程来执行此操作。

    DispatchQueue.main.async(execute: {
        self.label.text = "some text"
    })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多