Ios 在Swift中的不同线程上加载UI

Ios 在Swift中的不同线程上加载UI,ios,swift,xcode,storyboard,background-thread,Ios,Swift,Xcode,Storyboard,Background Thread,我有一个具有复杂UI视图的子控制器。我的视图是在故事板中设计的。这会导致屏幕冻结,如果用户继续点击冻结的屏幕,则会增加崩溃的可能性 我是否可能在不同的线程中加载UI并向用户显示活动指示器 我知道中间的复选框是兼容的。复选框是自定义的UI按钮。我在drawRect上画它们。根据选择、边框宽度、动态边框颜色、动态选定边框颜色、背景颜色、选择背景颜色 编辑: 请注意,superview标记不是500。这是一种多重选举观点 func setupCheckBox(checkbox: CheckBox) {

我有一个具有复杂UI视图的子控制器。我的视图是在故事板中设计的。这会导致屏幕冻结,如果用户继续点击冻结的屏幕,则会增加崩溃的可能性

我是否可能在不同的线程中加载UI并向用户显示活动指示器

<>我知道中间的复选框是兼容的。复选框是自定义的UI按钮。我在drawRect上画它们。根据选择、边框宽度、动态边框颜色、动态选定边框颜色、背景颜色、选择背景颜色

编辑: 请注意,superview标记不是500。这是一种多重选举观点

func setupCheckBox(checkbox: CheckBox) {
    checkbox.setCornerRadius(radius: CGSize(width: checkbox.frame.size.width * 0.5, height: checkbox.frame.size.height * 0.5))
    checkbox.setBorderWidth(width: 2.0)
    checkbox.setBorderColor(border_color: UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0))
    checkbox.setSelection(selection_color: UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 0.9))
    checkbox.setSelected(selection: false)
    checkbox.setHit(edgeInsets: UIEdgeInsets(top: -4, left: -4, bottom: -4, right: -4))
    checkbox.delegate = self
}
复选框实现:

圆角实施:


苹果的文档说

如果您的应用程序具有图形用户界面,建议使用 接收与用户相关的事件并启动界面更新 从应用程序的主线程。这种方法有助于避免 与处理用户事件和事件相关的同步问题 绘图窗口内容。一些框架,如Cocoa,通常 需要这种行为,但即使是那些不需要的,也要保持这种行为 主线程上的行为具有简化逻辑的优点 用于管理用户界面


有关详细信息,

任何时候,当您需要触摸涉及UI的任何内容时,请将该代码放入到主队列的分派中。对于Swift 3,请参阅新的DispatchQueue机制,否则,请使用良好的ol'dispatch\u async。。。并提供主队列。这方面的一个很好的教程是

我有一个具有复杂UI视图的子控制器

你在这里展示的并没有那么复杂。我想你的意思是,计算所有东西的去向是复杂的。如果这是真的,则需要计算绘图周期以外的时间

这会导致屏幕冻结,如果用户继续点击冻结的屏幕,则会增加崩溃的可能性

如果由于用户与冻结视图交互而导致崩溃,那么您的问题并不是您所认为的问题。如果挂起事件循环,所有用户触摸都将被忽略,因为它们也在事件循环中。但是,正如我所记得的那样,如果你花太长时间从事件循环返回,你可能会遇到崩溃,尽管现在可能更短。见上文;您必须将任何复杂的计算移到其他地方

<>我知道中间的复选框是兼容的。复选框是自定义的UI按钮。我在drawRect上画它们。根据选择、边框宽度、动态边框颜色、动态选定边框颜色、背景颜色、选择背景颜色

好极了。找出问题发生的地方是大多数解决方案。不过,您的drawRect对于这些应用程序来说应该非常简单。它们只是圆圈。如果您的drawRect除了检查预先计算的值之外,还执行其他操作,那么您需要将所有计算移到其他地方。drawRect通常不应该计算太多。它必须非常快

如果您的问题是要计算的内容很多,而且计算本身需要很长时间,那么您需要将其移动到后台队列,在计算时显示占位符视图,并在完成后绘制真实视图

如果您的问题是子视图比我们看到的要多得多,您可能需要减少子视图的数量并进行更多的自定义绘图,但是您提供的屏幕截图看起来并不复杂

但在一天结束时,您必须更新主队列上的UI,并且不能阻止主队列。因此,如果您有任何复杂的事情要做,您需要在另一个队列上完成,并在完成时更新UI


如果您的问题是从情节提要中拖出内容所需的实际时间,您可以尝试将此片段提取到自己的情节提要或笔尖中,或者以编程方式绘制它,或者只是减少情节提要中的元素数量,但我个人从未遇到过这样的情况。

请参阅:第一,ty了解信息。第二,是的,我知道。我们不应该从后台线程更新ui。我的问题不同。用户界面本身很复杂,我已经给你。。。我知道的一件事是,如果应用程序没有例外,并且没有大量的数据,那么当点击应用程序的不同屏幕时,它将永远不会崩溃。这取决于应用程序的逻辑。我可以通过使用dispatch禁用当前视图来解决崩溃问题。但是,我不能解决这个问题。在iPhone4上,它至少需要5秒。你应该真正使用仪器来找出应用程序到底在哪里暂停,然后看看你是否可以改进负责的代码。在我注入用户数据之前,我的UI本身很复杂。要我深入了解你写的内容吗?如果你正在用复杂的视图更新你的用户界面,唯一显示活动指示器块用户界面并在
只有主线程。好的,我可以设置用户交互。我可以显示活动指示器。但冻结问题不会得到解决。活动指示器本身将被冻结。对吗?如果我在调度中执行,活动指示器将显示没有问题。但是,它也会被冻结。谢谢你的帮助。还有其他想法吗?问题是所有方法都需要不必要的布局调用。我把它取了下来,效果很好。强制调用drawrect setneedsdisplay就足够了。我需要帮助。应用程序在所有设备上运行缓慢。我没有触摸任何东西。视图很重,它会冻结屏幕直到加载。我想解决冻结问题。有什么想法吗?是的,任何时候你更新任何涉及UI的东西,从主队列开始。任何繁重的处理、网络请求等都应该从后台队列中完成。一旦这些后台队列上的处理完成,通常会分派到主队列以更新UI(如活动微调器)。如果你在主队列上执行任何任务,请将它们放入后台队列。你还没有了解情况,兄弟。UI很复杂,它加载在主线程中。但是,因为它很重,所以会冻结屏幕。我不在主线程上做任何处理或http连接。你发送的屏幕截图中的UI本身看起来不太重;我已经创造了许多类似的布局和这样没有任何问题。也许你可以从你的管理员那里发布相关代码,否则我想这里的任何人都不知道该如何帮助。一个沉重的UI也可能是你当前视图控制器做得太多的一个指标,你可能会考虑将行为分解成单独的控制器/场景。11我不做任何计算的东西去哪里。所有的故事板都与嵌入的序列相连。我发现我指出的撞车事件是无效的。这并不是我重新调查的坠机原因。我将把它从问题中移除,为什么你说我知道顺从性在中间的复选框中?你这是什么意思?问题是所有方法都需要不必要的layoutifn调用。我把它取了下来,效果很好。强制调用drawrect setneedsdisplay就足够了。我需要帮助。应用程序在所有设备上运行速度都很慢。@hasan83啊,你几乎不应该直接调用layoutIfNeeded,除非你真的需要在进入下一行代码之前计算布局,这通常是因为你已经在使用布局代码了。你几乎总是说setNeedsLayout,即使这样也常常不需要。
protocol CheckBoxProtocol: class {
    func checkbox(checkBox: UIView, selection: Bool)
}

class CheckBox: RoundedCornersButton {

    var checked_icon: String?
    var unchecked_icon: String?

    weak var delegate: CheckBoxProtocol?

    var selectedd: Bool = false
    var allow_none: Bool = false
    var hitEdgeInsets: UIEdgeInsets?

    var selection_color: UIColor?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        setHit(edgeInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0))

        selection_color = .clear
        backgroundColor = .clear
        addTarget(self, action: #selector(didTouchButton), for: .touchUpInside)
    }

    func setCheckedIcon(checked_icon: String) {
        self.checked_icon = checked_icon
        setSelectionTo(button: self, selected: selectedd, inform: nil)
    }

    func setUncheckedIcon(unchecked_icon: String) {
        self.unchecked_icon = unchecked_icon
        setSelectionTo(button: self, selected: selectedd, inform: nil)
    }

    func setSelection(selection_color: UIColor) {
        self.selection_color = selection_color
        setNeedsDisplay()
        layoutIfNeeded()
    }

    // if superview tag is equal to 500. all other checkbox in the superview will deselected.
    func didTouchButton() {
        if superview?.tag == 500 {
            for button: UIView in (superview?.subviews)! {
                if button is CheckBox && button != self {
                    setSelectionTo(button: button as! CheckBox, selected: false, inform:delegate)
                }
            }
            setSelectionTo(button: self as CheckBox, selected: allow_none ? (!selectedd) : true, inform:delegate)
        } else {
            setSelectionTo(button: self as CheckBox, selected: !selectedd, inform:delegate)
        }
    }

    func setSelectionTo(button: CheckBox, selected: Bool, inform: CheckBoxProtocol?) {
        button.selectedd = selected
        if selected {
            if checked_icon != nil {
                (button as CheckBox).setImage(UIImage.init(named: checked_icon!), for: .normal)
            }

            if color != .clear {
                button.setTitleColor(color, for: .normal)
            }
        } else {
            if unchecked_icon != nil {
                (button as CheckBox).setImage(UIImage.init(named: unchecked_icon!), for: .normal)
            }

            if selection_color != .clear {
                button.setTitleColor(selection_color, for: .normal)
            }
        }

        button.setNeedsDisplay()
        button.layoutIfNeeded()
        inform?.checkbox(checkBox: button, selection: selected)
    }

    func setSelected(selection: Bool) {
        super.isSelected = selection
        setSelectionTo(button: self, selected: selection, inform: nil)
        setNeedsDisplay()
        layoutIfNeeded()
    }

    func setHit(edgeInsets: UIEdgeInsets)
    {
        hitEdgeInsets = edgeInsets
    }

    // handeling hits on checkbox. taking in count the hitEdgeInsets. if hitEdgeInsets have minus values hits around the checkbox will be considered as a valid hits.
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        if UIEdgeInsetsEqualToEdgeInsets(hitEdgeInsets!, .zero) || !isEnabled || isHidden {
            return super.point(inside: point, with: event)
        }

        let relativeFrame: CGRect = self.bounds
        let hitFrame: CGRect = UIEdgeInsetsInsetRect(relativeFrame, hitEdgeInsets!)

        return hitFrame.contains(point)
    }

    func isSelectedd() -> Bool {
        return selectedd
    }

    func setAllowNone(_ allow: Bool) {
        allow_none = allow
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)

        let context: CGContext = UIGraphicsGetCurrentContext()!

        context.setFillColor((selection_color?.cgColor)!)
        context.setStrokeColor((selection_color?.cgColor)!)

        let circlePath: UIBezierPath = UIBezierPath.init(arcCenter: CGPoint(x: frame.size.width * 0.5, y: frame.size.width * 0.5), radius: frame.size.width * 0.5 - borderWidth, startAngle: 0, endAngle: CGFloat(2.0 * Float(M_PI)), clockwise: true)

        if isSelectedd() {
            circlePath.fill()
            circlePath.stroke()
        }
    }
}
class RoundedCornersButton: UIButton {

    var cornorRadius: CGSize!
    var borderWidth: CGFloat!
    var borderColor: UIColor!

    var color: UIColor!

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        cornorRadius = CGSize(width: 12, height: 12)
        borderWidth = 0
        borderColor = UIColor.clear

        self.titleLabel?.numberOfLines = 1
        self.titleLabel?.adjustsFontSizeToFitWidth = true
        self.titleLabel?.lineBreakMode = .byClipping
        self.titleLabel?.baselineAdjustment = .alignCenters
        self.titleLabel?.textAlignment = .center

        // clear the background color to draw it on the graphics layer
        color = self.backgroundColor
        self.backgroundColor = UIColor.clear
    }

    func setColor(_color: UIColor) {
        color = _color
        self.setNeedsDisplay()
        self.layoutIfNeeded()
    }

    func setCornerRadius(radius: CGSize) {
        cornorRadius = radius
        self.setNeedsDisplay()
        self.layoutIfNeeded()
    }

    func setBorderWidth(width: CGFloat) {
        borderWidth = width
        self.setNeedsDisplay()
        self.layoutIfNeeded()
    }

    func setBorderColor(border_color: UIColor) {
        borderColor = border_color
        self.setNeedsDisplay()
        self.layoutIfNeeded()
    }

    // redraw without increasing or decreasing hiting area.
    override func draw(_ rect: CGRect) {
        // Drawing code
        super.draw(rect)

        // drawing context
        let context: CGContext = UIGraphicsGetCurrentContext()!

        // set fill color
        context.setFillColor(color.cgColor)

        // theme color
        let themeColor: UIColor = borderColor

        // set stroke color
        context.setStrokeColor(themeColor.cgColor.components!)

        // corners to round
        let corners: UIRectCorner = UIRectCorner.allCorners

        // bezier path rect

        let bezierRect: CGRect = CGRect(x: rect.origin.x+borderWidth*0.5, y: rect.origin.y+borderWidth*0.5, width: rect.size.width-borderWidth, height: rect.size.height-borderWidth)

        // rounded path
        let roundedPath: UIBezierPath = UIBezierPath.init(roundedRect: bezierRect, byRoundingCorners: corners, cornerRadii: cornorRadius)

        // set stroke width
        roundedPath.lineWidth = borderWidth

        // fill coloring
        roundedPath.fill()

        // stroke coloring
        roundedPath.stroke()
    }
}