我有一个具有复杂UI视图的子控制器。我的视图是在故事板中设计的。这会导致屏幕冻结,如果用户继续点击冻结屏幕,则会增加崩溃的可能性。
是否可以在不同的线程中加载UI并向用户显示活动指示符?
我知道complixity位于中间的复选框中。复选框是一个自定义的uibuttons。我在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
}
CheckBox实施:
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()
}
}
答案 0 :(得分:2)
每当您需要触摸涉及UI的任何内容时,请将该代码放入主队列的调度中。对于Swift 3,请参阅新的DispatchQueue机制,否则,请使用好的'dispatch_async(...)并提供主队列。一个很好的教程是https://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1。
答案 1 :(得分:1)
Apple文档说,
“如果您的应用程序具有图形用户界面,建议您使用 您收到与用户相关的事件并启动界面更新 从您的应用程序的主线程。这种方法有助于避免 与处理用户事件相关的同步问题 绘图窗口内容。一些框架,例如Cocoa 需要这种行为,但即使对于那些不这样做,也要保持这种行为 主线程上的行为具有简化逻辑的优点 用于管理用户界面。“
有关详细信息,请Click here
答案 2 :(得分:1)
我有一个具有复杂UI视图的子控制器。
你在这里展示的并不复杂。我猜你的意思是计算一切都很复杂。如果这是真的,您需要计算除绘制周期之外的一些时间。
这导致屏幕冻结,如果用户继续点击冻结的屏幕,则会增加崩溃的可能性。
如果由于用户与冻结视图交互而导致崩溃,那么您的问题就不是您认为的那样。如果您挂起事件循环,则将忽略所有用户触摸(因为它们也在事件循环中)。但是如果你花了很长时间从事件循环中返回,你可能会崩溃(我记得~~ 2s,尽管这些日子可能会更短)。往上看;你必须在其他地方移动任何复杂的计算。
我知道complixity位于中间的复选框中。复选框是一个自定义的uibuttons。我在drawRect上绘制它们。取决于选择,边框宽度,动态边框颜色,动态选定边框颜色,背景颜色,选择背景颜色。
优异。追逐问题发生的地方是大多数解决方案。不过,你的drawRect
对于这些应该是非常微不足道的。他们只是圈子。如果您的drawRect
正在执行除检查预先计算的值之外的任何操作,那么您需要将所有计算移动到其他位置。 drawRect
通常不应该计算得太多。它必须非常快。
如果您的问题是需要计算很多并且计算本身需要很长时间,那么您需要将其移动到后台队列,在计算它时显示占位符视图,以及何时完成绘制真实的观点。
如果您的问题是我们看到的子视图数量多得多,您可能需要减少子视图的数量并进行更多自定义绘制(但您提供的屏幕截图看起来并不复杂)。
但是在一天结束时,必须更新主队列上的UI,并且必须阻止主队列。因此,如果您有任何复杂的事情要做,您需要在另一个队列上执行此操作并在完成后更新UI。
如果您的问题是从Storyboard中取出内容所需的实际时间,您可以尝试将此片段提取到自己的Storyboard或NIB中,或者以编程方式绘制它(或者只是减少Storyboard中的元素数量),但是我从来没有亲自遇到过这样的问题。